Reputation: 20780
Let's assume all the numbers in this world are positive integers and they can be represented by uintX_t C++ types.
Let's consider next awesome code to convert a std::string to a number:
#include <string>
#include <cstdint>
#include <iostream>
template <typename T>
T MyAwsomeConversionFunction(const std::string& value)
{
T result = 0;
for(auto it = value.begin(); it != value.end() && std::isdigit(*it); ++it)
{
result = result * 10 + *it - '0';
}
return result;
}
int main(int argc, const char * argv[])
{
std::cout<<MyAwsomeConversionFunction<uint16_t>("1234")<<std::endl;
std::cout<<MyAwsomeConversionFunction<uint16_t>("123456")<<std::endl;
return 0;
}
As you can see there are multiple errors in this function, but I'm interested in a particular one: How to detect when my type is not large enough to contain the value (second conversion call as example) and avoid UB when making result = result * 10 + *it - '0';
. I would like to know if that operation will exceed maximum value of T
before making it. Is this possible?
EDIT: please check Is signed integer overflow still undefined behavior in C++? for more info about UB on arithmetic operations in C++. I want to avoid executing the line result = result * 10 + *it - '0';
when the result will overflow. In the answer the line is still executed...
EDIT2: I found the answer here: How to detect integer overflow?
EDIT3: The accepted answer applies for signed types. For unsigned types Cheers and hth. - Alf answer is correct.
Upvotes: 1
Views: 137
Reputation: 42805
You just need to work backwards, asking if a given digit will overflow:
// When result exceeds this thresh, appending a digit will always overflow.
static const T thresh = std::numeric_limits<T>::max() / 10;
// When result equals this thresh, appending a digit larger than
// thresh_last_digit will overflow.
static const T thresh_last_digit = std::numeric_limits<T>::max() - 10 * thresh;
for(auto it = value.begin(); it != value.end() && std::isdigit(*it); ++it)
{
if(result > threshold)
throw std::overflow_error(value);
T digit = *it - '0';
if(result == threshold && digit > thresh_last_digit)
throw std::overflow_error(value);
result = result * 10 + digit;
}
Upvotes: 1
Reputation: 62083
I'll take a whack at this, although I may get picked apart for mistakes. This does not deal with negative values in the string (your original code doesn't either). And it's limited to ASCII digits, as Alf mentioned in a comment on his answer.
template <typename T>
T MyAwsomeConversionFunction(const std::string& value)
{
T maxBeforeMult = std::numeric_limits<T>::max / 10;
T result = 0;
for(auto it = value.begin(); it != value.end() && std::isdigit(*it); ++it)
{
// Check if multiplying would overflow
if (result > maxBeforeMult)
{
// throw overflow
}
result = result * 10;
T digit = *it - 0;
// Check if adding would overflow
if (std::numeric_limits<T>::max - result < digit)
{
// throw overflow
}
result += digit;
}
return result;
}
Upvotes: 1
Reputation: 145279
For unsigned type T
you can always do
T const original = result;
result = result * 10 + *it - '0';
if( result / 10 != original ) { throw 666; }
Except, replace the throw 666
with something.
For the apparent original problem of converting string → integer with overflow detection, see strtol
and family.
Upvotes: 0