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

ご覧のとおり、この関数には複数のエラーがありますが、特定のエラーに興味があります: 型が値を格納するのに十分な大きさでない場合 (例として 2 番目の変換呼び出し) を検出し、作成時に UB を回避する方法result = result * 10 + *it - '0';Tその操作を行う前に最大値を超えるかどうかを知りたいです。これは可能ですか?

編集: Is signed integer overflow still undefined behavior in C++?を確認してください。C++ の算術演算に関する UB の詳細については、result = result * 10 + *it - '0';結果がオーバーフローするときに行を実行することを避けたい。答えでは、行はまだ実行されています...

EDIT2:ここで答えを見つけました:整数オーバーフローを検出する方法?

EDIT3: 受け入れられた回答は、署名された型に適用されます。unsigned 型の場合 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を何かに置き換えます。


オーバーフロー検出を使用して文字列を整数に変換するという明らかな元の問題については、strtoland ファミリを参照してください。

于 2014-07-01T14:41:45.997 に答える