Mircea Ispas
Mircea Ispas

Reputation: 20780

Type aware string to number conversion in C++

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

Answers (3)

Mike DeSimone
Mike DeSimone

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

Fred Larson
Fred Larson

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

Cheers and hth. - Alf
Cheers and hth. - Alf

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

Related Questions