0

Cでオーバーフローを検出する手法について読んでいました。さらにオーバーフローを検出するための誤った解決策を示す例の1つは、これです。

/* Determine whether arguments can be added without overflow */
int tadd_ok(int x, int y) {
    int sum = x+y;
    return (sum-x == y) && (sum-y == x);
}

そしてそれはそれが機能しないと言った:

2の補数の加算はアーベル群を形成するため、式(x + y)-xは、加算がオーバーフローするかどうかに関係なくyと評価され、その(x + y)-yは常にxと評価されます。

正確にはどういう意味ですか?Cコンパイラがに置き換えられるということsumですx+yか?
それが何を言っているのかを理解するために、私はプログラムのアセンブリコードをトレースしましたが、置き換えの兆候はありませんでした。

更新:私の質問の本質は、GCCは式を計算せずに評価するのかということです。
これは2の補数についての質問ではありません。ここで
サンプル出力を見ることができます。

4

5 に答える 5

2

些細な例をとる4 (0b0100) + 5 (0b0101)と、符号なしの合計9 (1001)は実際-7には2の補数である必要があることがわかります。次に、その合計(0b1001)を取得し、2の補数演算を使用して4を減算すると、次のようになります。

    0b1001 - 0b0100 = 0b1001 + 2s_complement(0b0100) = 0b1001 + 0b1100 = 0b1_0101 

最終的には5である0101になります(2の補数演算中にオーバーフローした最上位の1を削除します)。合計から5を引くと、4になります。

    0b1001 - 0b0101 = 0b1001 + 2s_complement(0b0101) = 0b1001 + 0b1011 = 0b1_0100

これは、指定したcコードを満たしますが、それでもオーバーフローが発生します。

2の補数に関するウィキペディアの記事から:

2の補数10進数
01117
01106
0101 5
0100 4
0011 3
0010 2
0001 1
0000 0
1111 -1
1110 −2
1101 −3
1100 -4
1011 -5
1010 -6
1001 −7
1000 −8

更新:
INT_MAX = 7の簡単な4ビット整数システムを使用したINT_MAXの例を示すために、cコードと同じ結果を確認できます。

    7 + 7 (0b0111 + 0b0111) = 0b1110 (-2 in two's complement)

上記の私の例のように、減算すると、sum - 7に等しくなり7ます。

    0b1110 - 0b0111 = 0b1110 + 2s_complement(0b0111) = 0b1110 + 0b1001 = 0b1_0111
于 2012-07-13T19:03:57.477 に答える
0

私は間違っているかもしれませんが、これは単純なオーバーフローチェックのように思えます...合計がオーバーフローした場合、sum-xはyと等しくなりませんでした

さらに何か:パラメータはスタックで関数に渡され、sumはローカル変数であり、スタックにもあります。コンパイラーは、sum = 0、sum + x、およびそれらのsum + y(スタック位置+別のスタック位置)をsum位置に配置する可能性があります。returnステートメントの場合、sum変数を複製することにより、結果を別の一時的な場所に配置する可能性があります。

于 2012-07-13T18:42:58.733 に答える
0

誤った結果を引き起こすエラーは、後続のテストの両側にも影響を与えるように見えるため、テストが失敗することはありません。

于 2012-07-13T18:46:36.097 に答える
0

コンパイラに最適化がある場合(つまり-02)、この関数が機能しない可能性があります。ただし、最適化がない場合は、これで正しく機能すると思います。

于 2012-07-13T18:47:08.250 に答える
0

投稿されたコードに関しては、一部のプラットフォームが暗黙的にオーバーフローをラップアラウンドするため、すべてのプラットフォームで機能するとは限りません。式は次のように評価されます。

int x    = 0xFFFFFFF0;
int y    = 0x00000020;

int sum  = x + y;   // value of sum is 0x00000010
                    // would be 0x100000010, but highest order bit is dropped

int diff = sum - y; // value of diff is 0xFFFFFFF0
                    // equal to x

ドメインは閉じられています。つまり、オーバーフローは予測可能で可逆的な方法でラップアラウンドします。このメソッドに依存してオーバーフローをチェックすることはできません。

とはいえ、この動作はプラットフォーム固有です。一部のプロセッサはこの方法でこれを実行し、他のプロセッサは可能な限り最も近い表現可能な値を返します(つまり、合計は0xFFFFFFFFに等しくなります)。この場合、期待どおりに機能します。

Cのオーバーフローをチェックする最良の方法は、インラインアセンブリステートメントを使用してフラグをスタックにプッシュし、それらをレジスタにポップして、値を返すことです。最後の数学演算でオーバーフローが発生した場合、フラグの1つが設定されます。私は以前にこのようにそれをしましたが、それを行う方法を覚えていません。リソースとヘルプについては、次のページを参照してください。

http://en.wikipedia.org/wiki/FLAGS_register_(computing)

Cプログラムからフラグレジスタを読み取ります

于 2012-07-13T18:51:30.313 に答える