2

次のコードを検討してください。

#include <iostream>
using namespace std;

int main() {
    // the following is expected to not print 4000000000
    // because the result of an expression with two `int`
    // returns another `int` and the actual result 
    // doesn't fit into an `int` 
    cout << 2 * 2000000000 << endl; // prints -294967296

    // as such the following produces the correct result
    cout << 2 * 2000000000U << endl; // prints 4000000000
}

結果をさまざまな整数型にキャストして少し遊んだところ、奇妙な動作に出くわしました。

#include <iostream>
using namespace std;

int main() {
    // unexpectedly this does print the correct result
    cout << (unsigned int)(2 * 2000000000) << endl; // prints 4000000000

    // this produces the same wrong result as the original statement
    cout << (long long)(2 * 2000000000) << endl; // prints -294967296
}

次の両方のステートメントで正しい結果が得られないと予想しましたが、なぜ一方が成功し、もう一方が失敗したのでしょうか。

4

5 に答える 5

4

この質問に答えようとしている人々には、あまりにも多くの混乱が起こっています。

調べてみましょう:

2 * 2000000000

これに。をint掛けintます。§5/4は私たちに教えています:

式の評価中に、結果が数学的に定義されていないか、そのタイプの表現可能な値の範囲内にない場合、動作は定義されていません。

この結果は数学的に定義されていますが、?の表現可能な値の範囲内にありますintか?

場合によります。多くの一般的なアーキテクチャintでは、値を表す32ビットがあり、最大値は2,147,483,647になります。これの数学的結果は4,000,000,000であるため、このようなアーキテクチャでは値を表すことができず、動作は定義されていません。(プログラム全体の動作が未定義になっているため、これで問題はほとんどなくなります。)

しかし、それはプラットフォームに依存しています。代わりに64ビット幅の場合int(注:long long値を表すために少なくとも64ビットが保証されています)、結果はうまく適合します。

問題を少し修正して、これに直接進みましょう。

int x = -294967296; // -294,967,296

intさらに、これが(32ビットの場合は)の範囲内に収まるとしましょうint

これを:にキャストしましょうunsigned int

unsigned int y = static_cast<unsigned int>(x);

の価値はy何ですか?のビット表現とは何の関係もありませんx

コンパイラが単にビットを符号なしの量として扱う「ビットキャスト」はありません。変換はで機能します。に変換された値§4.7 /2で定義されています。signed intunsigned int

宛先タイプが符号なしの場合、結果の値は、ソース整数と一致する最小の符号なし整数です(モジュロ2 n、nは符号なしタイプを表すために使用されるビット数)。[注:2の補数表現では、この変換は概念的なものであり、ビットパターンに変更はありません(切り捨てがない場合)。—エンドノート]

32ビット(unsignedintシステムでは、これは4000000000を意味します。これは、ビットに関係なく機能します。2の補数、1の補数、魔法の補数などです。これらは関係ありません。

最初の部分(UBを無視)に必要な値が表示される理由は、2つの補数のマシンでは、符号付き整数と符号なし整数の違いは、実際にはビットの表示方法が異なるためです。したがって、これら2つを乗算するとint、2つの符号なし整数を「実際に」乗算し、オーバーフローを無視して、結果を符号付き整数として表示していました。その後、キャストはもう一度あなたの見方を変えます。

しかし、キャストはビットとは独立して機能します!

于 2013-01-15T20:43:29.973 に答える
3

intでは、の値4,000,000,000は次のように記述されます。1110 1110 0110 1011 0010 1000 0000 0000

unsigned intでは、の値4,000,000,000は次のように記述されます。1110 1110 0110 1011 0010 1000 0000 0000

これら2つを見ると、同じであることがわかります。

違いは、ビットがaintとで読み取られる方法にありunsigned intます。通常int最上位ビットは、数値が負であるかどうかを判断するために使用されます。

于 2013-01-15T19:46:36.583 に答える
1

C ++では、式のタイプはコード環境に依存しません(通常)。

したがって、部分式2 * 2000000000は、含まれている式のコンテキストに関係なく、同じシステム上で同じタイプと値を持ちますint(*演算子の両方のオペランドがintsであるため)。そしてそれは4000000000になりますが、あなたのアーキテクチャでは、オーバーフローのために-294967296に変更されました。

にキャストしてlong longも値は変更されません。これは、long longが-294967296を適切に表すことができるためです。

実際、機能する方がはるかに興味深いですcout << (unsigned int)(2 * 2000000000) << endl;。-294967296unsinged intを保持できないため、オーバーフローが再び発生します。-294967296と4000000000は2^32を法として合同であるため、これが新しい値になります。(GManNickGのより良い答えから更新されました)。

より深い問題を説明するためにあなたは試すことができます

cout << (unsigned int)(2 * 2000000000 / 2) << endl;

除算は-294967296で実行され、-147483648のバイナリ表現は符号なしに変換されます(4147483648)。

于 2013-01-15T19:49:32.870 に答える
0

3番目の(奇妙な)ケースでは、実行中のプログラムはこれを行います:

2 * 2000000000       = binary number (11101110011010110010100000000000)
print it as unsigned = 4000000000 
                   (interprets the first bit (1) as part of the unsigned number)

4番目のケース:

2 * 2000000000       = binary number (11101110011010110010100000000000, same as above) 
print it as signed   = -294967296 
                   (interprets the first bit (1) as negative number)

学ぶべき重要なことは、式2 * 2000000000はバイトシーケンスになり、キャスト操作が言うように解釈されるということです。

于 2013-01-15T19:57:52.587 に答える
0

符号付き整数のオーバーフローは未定義の動作であることに注意してください。結論として、何でも起こり得ます。無邪気に正しい結果を含みます。


両方の整数リテラル2200000000032ビット幅です。コンパイラが指示するように、結果はオーバーフローします。

warning: integer overflow in expression [-Woverflow]

乗算の結果は、32ビットの符号付き整数のままです。そして、この場合、オーバーフローの結果は、符号なし32ビット整数として表示されたときに幸運にも正しい結果になります。これは、ビットパターンを32ビットにキャストするときに確認できますunsigned int

ただし、値をより広い幅の整数型(64ビットなど)にキャストすると、先頭のバイトにff符号拡張)が埋め込まれるため、誤った結果が得られます。

#include <iostream>

int main() {
    long long x = 2 * 2000000000;     // 8 byte width
    unsigned int y = 2 * 2000000000;  // 4 byte width
    unsigned long z = 2 * 2000000000; // 8 byte width
    std::cout << std::hex << x << " " << std::dec << x << std::endl;
    // output is: ffffffffee6b2800 -294967296
    std::cout << std::hex << y << " " << std::dec << y << std::endl;
    // output is: ee6b2800 4000000000
    std::cout << std::hex << z << " " << std::dec << z << std::endl;
    // output is: ffffffffee6b2800 18446744073414584320

}
于 2013-01-15T20:07:31.043 に答える