一見すると、この質問は整数オーバーフローを検出する方法の複製のように見えるかもしれません。ただし、実際には大幅に異なります。
符号なし整数オーバーフローの検出は非常に簡単ですが、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)オーバーフローチェックを最適化する機会をコンパイラに提供しないことが保証されているものはありますか?両方のオペランドをunsignedにキャストし、独自の2の補数演算をロールしてチェックを実行することで、それを行う方法があるのではないかと考えていましたが、その方法がよくわかりません。