1

让我们假设这个世界上的所有数字都是正整数,它们可以用 uintX_t C++ 类型表示。

让我们考虑下一个将 std::string 转换为数字的很棒的代码:

#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;
}

如您所见,此函数中有多个错误,但我对一个特定的错误感兴趣:如何检测我的类型何时不足以包含该值(例如第二次转换调用)并在制作result = result * 10 + *it - '0';. 我想知道该操作是否会T在进行之前超过最大值。这可能吗?

编辑:请检查是否有符号整数溢出在 C++ 中仍然是未定义的行为?有关 C++ 中算术运算的 UB 的更多信息。我想避免result = result * 10 + *it - '0';在结果溢出时执行该行。在答案中,该行仍在执行...

EDIT2:我在这里找到了答案:如何检测整数溢出?

EDIT3:接受的答案适用于签名类型。对于无符号类型 Cheers 和 hth。- 阿尔夫的答案是正确的。

4

3 回答 3

1

尽管我可能会因为错误而被分开,但我会对此进行打击。这不处理字符串中的负值(您的原始代码也不处理)。正如 Alf 在对他的回答的评论中提到的那样,它仅限于 ASCII 数字。

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;
}
于 2014-07-01T15:55:10.770 回答
1

您只需要向后工作,询问给定的数字是否会溢出:

// 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;
}
于 2014-07-01T15:55:51.633 回答
0

对于无符号类型T,您总是可以这样做

T const original = result;
result = result * 10 + *it - '0';
if( result / 10 != original ) { throw 666; }

除了,throw 666用一些东西替换。


对于使用溢出检测转换字符串→整数的明显原始问题,请参阅strtol和系列。

于 2014-07-01T14:41:45.997 回答