一見すると、この質問は整数オーバーフローを検出する方法の重複のように思えるかもしれません。ですが、実際には大きく異なります。
符号なし整数のオーバーフローを検出することは非常に簡単ですが、C/C++ で符号付きオーバーフローを検出することは、実際にはほとんどの人が考えるよりも難しいことがわかりました。
それを行うための最も明白でありながら単純な方法は、次のようなものです。
int add(int lhs, int rhs)
{
int sum = lhs + rhs;
if ((lhs >= 0 && sum < rhs) || (lhs < 0 && sum > rhs)) {
/* an overflow has occurred */
abort();
}
return sum;
}
これに関する問題は、C 標準によると、符号付き整数のオーバーフローが未定義の動作であることです。 言い換えれば、標準によれば、符号付きオーバーフローを引き起こすとすぐに、プログラムは null ポインターを逆参照した場合と同じように無効になります。したがって、未定義の動作を引き起こすことはできず、上記の事後条件チェックの例のように、事後にオーバーフローを検出しようとします。
上記のチェックは多くのコンパイラで機能する可能性がありますが、期待することはできません。実際、C 標準では符号付き整数のオーバーフローは未定義であると述べられているため、最適化フラグが設定されている場合、一部のコンパイラ (GCC など) は上記のチェックを最適化して除外します。これにより、オーバーフローをチェックする試みが完全に中断されます。
したがって、オーバーフローをチェックする別の方法は次のとおりです。
int add(int lhs, int rhs)
{
if (lhs >= 0 && rhs >= 0) {
if (INT_MAX - lhs <= rhs) {
/* overflow has occurred */
abort();
}
}
else if (lhs < 0 && rhs < 0) {
if (lhs <= INT_MIN - rhs) {
/* overflow has occurred */
abort();
}
}
return lhs + rhs;
}
このような加算を実行してもオーバーフローが発生しないことを事前に確認するまで、2 つの整数を実際に加算しないため、これはより有望に思えます。したがって、未定義の動作は発生しません。
ただし、残念ながら、このソリューションは、加算演算が機能するかどうかをテストするためだけに減算演算を実行する必要があるため、最初のソリューションよりも効率が大幅に低下します。この (小さな) パフォーマンス ヒットを気にしないとしても、このソリューションが適切であると完全に確信しているわけではありません。この式lhs <= INT_MIN - rhs
は、符号付きオーバーフローが不可能であると考えて、コンパイラが最適化して取り除く可能性のある式とまったく同じように見えます。
ここでより良い解決策はありますか?1) 未定義の動作を引き起こさないこと、および 2) オーバーフロー チェックを最適化する機会をコンパイラに提供しないことが保証されているものはありますか? 両方のオペランドを符号なしにキャストし、独自の 2 の補数演算をローリングしてチェックを実行する方法があるのではないかと考えていましたが、その方法がよくわかりません。