2013-04-10 22 views
22

私は2つの数字:ABを持っています。私はコードのどこかにA+Bを計算する必要があります。 ABの両方がlong longであり、の正または負の値です。A + Bがlong longを超えているかどうかをチェックする方法? (AとBの両方が長いです)

コードが正しく実行されず、A+Bを計算するときに問題が発生している可能性があります。 A+Blong longの範囲を超えているかどうかを確認したいだけです。だから、私はデバッグのためだけに使うので、どんな方法でも問題ありません。

+0

'場合((A <0 && B <0 && A+B> 0)||(A> 0 && B> 0 && A + B <0) ){/ * Overflow * /} 'AとBの両方に同じ記号がある場合、オーバーフローすることができます。もしそうで、A + Bに同じ符号がないなら、オーバーフローの問題があります。他のニュースでは、これは基本的には最速のメソッドです。一定の時間内に実行されるため、一度だけ追加します。 – FrankieTheKneeMan

+2

回答として投稿する必要があります。ところで、 'A = B = 0x8000000000000000'、次に' A + B = 0'とコードが機能しないコーナーケースがあります:p – riv

+1

@FrankieTheKneeMan整数オーバーフローは未定義の動作です。 – john

答えて

31

両方の数値が同じ符号を持つ場合にのみオーバーフローが可能です。両方が正である場合、数学的にはA + B > LLONG_MAX、またはそれと同等の場合は、B > LLONG_MAX - Aでオーバーフローします。右辺は負ではないので、後者の条件はすでにB > 0を意味します。同様の議論では、否定的なケースについては、Bの符号を確認する必要はありません(Ben Voigtのおかげで、Bの符号チェックが不要であることを指摘しています)。次に、オーバーフローを検出するために

if (A > 0) { 
    return B > (LLONG_MAX - A); 
} 
if (A < 0) { 
    return B < (LLONG_MIN - A); 
} 
return false; 

をチェックすることができます。これらの計算は、最初のチェックのためにオーバーフローすることはありません。

A + Bの結果の符号を確認すると、オーバーフローする整数計算のラップアラウンドセマンティクスが保証されます。しかし、符号付き整数のオーバーフローは未定義の動作であり、ラップアラウンドが実装された動作のCPUであっても、コンパイラは未定義の動作が発生しないと想定し、オーバーフローチェックを完全に削除します。したがって、この質問に対するコメントで示唆された小切手は非常に信頼性が低い。次のような

+4

標識をチェックするだけでは動作する保証はありませんが、オプティマイザは保証の欠如を使用しています(たとえば、gccが 'a + 42> a'を' true'に最適化したことを確認しました)。 – AProgrammer

+0

これは、左右に 'return'を投げるのではなく、単一の式でラップした方がずっと読みやすくなります。 'のようなもの' return a <0!= b <0 || (a < 0 ? b > LLONG_MIN-a:b

+16

@JamesKanze私はそれを別々のケースに分割して読みやすくしていますが、もちろん私の好みです。 –

7

何か:

long long max = std::numeric_limits<long long>::max(); 
long long min = std::numeric_limits<long long>::min(); 

if(A < 0 && B < 0) 
    return B < min - A; 
if(A > 0 && B > 0) 
    return B > max - A; 

return false; 

次のように私たちは、このについて推論することができます

  • ABは反対の符号である場合、それらがオーバーフローすることはできません - ゼロが必要とするよりも1の方が大きいですmaxより大きくなるか、または0より小さい値をminより小さくする必要があります。

  • その他の場合、単純な代数で十分です。 A + B > max => B > max - Aが両方とも正の場合はオーバーフローします。そうでない場合は、両方とも負の場合、A + B < min => B < min - A。また

+0

'B> 0'と' B <0'テストは冗長です。 –

2

、あなただけのデバッグのためにそれを使用している場合、あなたは直接、最後の操作からオーバーフロービットを読み取るために、次の「ハック」を使用することができます(あなたのコンパイラ/ CPUを想定したが、これをサポートしています):

int flags; 
_asm { 
    pushf  // push flag register on the stack 
    pop flags // read the value from the stack 
} 
if (flags & 0x0800) // bit 11 - overflow 
    ... 
+0

キャリーフラグは、符号なしの数値では面白いです。符号付き数値の場合は、オーバーフローフラグをチェックする必要があります。 – fredoverflow

2

符号をマスクし、符号なしの値にキャストし、加算を実行します。 1 << (sizeof(int) * 8 - 1)を超えると、オーバーフローが発生します。

int x, y; 
if (sign(x) == sign(y)){ 
    unsigned int ux = abs(x), uy = abs(y);  
    overflow = ux + uy >= (1 << (sizeof(int) * 8 - 1)); 
} 

いっそのこと、のテンプレートを書いてみましょう:

template <typename T> 
bool overflow(signed T x, signed T y){ 
    unsigned T ux = x, uy = y; 
    return (sign(x) == sign(y) && (ux + uy >= (1 << (sizeof(T) * 8 - 1))); 
} 
関連する問題