web-dev-qa-db-ja.com

文字列を符号なし整数に変換すると、間違った結果が返されます

私は次の文字列を持っています:

sThis = "2154910440";

unsigned int iStart=atoi(sThis.c_str());

しかし、結果は

iStart = 2147483647

誰かが私の間違いを見ていますか?

18
tmighty

atoiは、文字列をintに変換します。システムでは、intは32ビットで、最大値は2147483647です。変換しようとしている値はこの範囲外なので、atoiの戻り値は未定義です。この場合、実装はintの最大値を返します。

代わりに atoll を使用できます。これはlong longを返し、少なくとも64ビットであることが保証されています。または、 stoi/stol/stoll ファミリ、または 符号なしの対応物 。実際には、例外の形式で範囲外の値(および無効な値)に関する有用なエラーレポートを提供します。

個人的には boost::lexical_cast 。少し面倒に見えますが、より一般的な状況で使用できます。テンプレートでそれを使用し、特殊化を持たなくても型引数を転送することができます

34

代わりに、std::strtoulにある<cstdlib>を使用する必要があります。これは、符号なしの数値用に設計されており、範囲が広く、エラーをより適切に報告します。

入力にstd::stringを使用し、エラー処理に例外を使用する場合は、std::stoulを使用します。短く、非常に効率的な実装は次のとおりです。

#include <string>
#include <stdexcept>
inline unsigned int stoui(const std::string& s)
{
    unsigned long lresult = stoul(s, 0, 10);
    unsigned int result = lresult;
    if (result != lresult) throw std::out_of_range();
    return result;
}

これはistringstream、culture-invariant(通常とは異なるロケールで実行したときの動作に予期しない変更がない)よりもはるかに高速で、完全に移植可能です。 0xおよび0プレフィックスの。

ただし、unsigned intは必ずしも値を保持するのに十分な大きさではないため、unsigned longを使用すると、上記のラッパーは不要になります。

9
Ben Voigt

atoiは符号付きintを返します。プラットフォームでは、2^31-1

その結果を何に割り当てるかは関係ありません。戻り値の型によって制限されます。

C++ストリームは、符号なし整数を読み取ることができます。

std::istringstream reader(sThis);
unsigned int val;
reader >> val;
7
helloworld922

忘れないでください正確にしたいことをする独自の関数をいつでも書くことができます。

このコードは、-9223372036854775806(2 ^ 63 + 1)から9223372036854775807(2 ^ 63-1)までの任意の数で機能します。

このようなもの:

long long int myAtoi ( string str ) {
    long long int value = 0;

    for (int i = 0; i < str.size(); i++) {

        if (str[i] != '-') {
            value *=  10;
            value += (int) ((str[i]) - '0');
        }
    }


    if (str.size() > 0 && str[0] == '-')
        return -value;
    else
        return value;
}
1
Oleksiy

多くの場合、符号なしintは最大4,294,967,295のC++の32ビット値です。したがって、2,154,710,440は符号なし整数として表すことができます。ただし、atoiは符号付きで最大値が2,147,483,647のintに変換されるため、文字列が値の範囲をオーバーフローするため、答えが正しくありません。文字列を少なくとも64ビットのlong longに変換する環礁を使用できます。 C++では、整数サイズはコンパイラに依存します。多くの場合、ヘッダーファイルstdint.hをインクルードしてからuint32_tまたはuint64_tなどを使用すると、処理しているサイズがわかります。

1
David Elliman

文字列をlong intに変換するatolを使用できます。詳細については、Linuxのman atolを参照してください。

プロトタイプ

#include <stdlib.h>
long atol(const char *nptr);
1
Madan Ram

残念ながら、C++には符号なしintを解析するための組み込み実装がなく、これは本当に奇妙です。

ここにあなたを助けることができるコードがあります:

#include <stdint.h>
#include <sstream>

inline unsigned int stoui(const std::string& s)
{
    std::istringstream reader(s);
    unsigned int val = 0;
    reader >> val;
    return val;
}

// This may be not the same as stoui on some platforms:
inline uint32_t stoui32(const std::string& s)
{
    std::istringstream reader(s);
    uint32_t val = 0;
    reader >> val;
    return val;
}
1
Sergey

このコードは、C++ 11で変換します。

std::string sThis = "2154910440";
unsigned int iStart = static_cast<unsigned int>(std::stoul(sThis));

std::stoulは、unsigned longよりも大きいunsigned intを返します。

static_castは、適切なタイプに変換します。

0
GizMoCuz