web-dev-qa-db-ja.com

Cで整数オーバーフローを検出する方法

数値が大きくなると、CPythonは整数を長整数(任意精度の演算を可能にする)に暗黙的に昇格することを知っています。

純粋なCでintおよびlong longのオーバーフローを検出するにはどうすればよいですか?

22
Dean

予測できますsigned int overflowしかし、合計の後にそれを検出しようとすると遅すぎます。署名付き加算を行う前に、オーバーフローの可能性をテストする必要があります。

合計後にテストして未定義の動作を回避することはできません。加算がオーバーフローする場合は、未定義の動作がすでにあります。

それが私なら、私はこのようなことをします:

#include <limits.h>

int safe_add(int a, int b) 
{
    if (a >= 0) {
        if (b > (INT_MAX - a)) {
            /* handle overflow */
        }
    } else {
        if (b < (INT_MIN - a)) {
            /* handle underflow */
        }
    }
    return a + b;
}

詳細については、この paper を参照してください。また、符号なし整数オーバーフローが未定義の動作ではない理由と、同じペーパーで移植性の問題となる可能性があることもわかります。

編集:

GCCおよびその他のコンパイラーには、オーバーフローを検出するためのいくつかの機能があります。たとえば、GCCには次の組み込み関数があり、演算がオーバーフローしたかどうかを確認しながら、簡単な算術演算を実行できます。

bool __builtin_add_overflow (type1 a, type2 b, type3 *res)
bool __builtin_sadd_overflow (int a, int b, int *res)
bool __builtin_saddl_overflow (long int a, long int b, long int *res)
bool __builtin_saddll_overflow (long long int a, long long int b, long long int *res)
bool __builtin_uadd_overflow (unsigned int a, unsigned int b, unsigned int *res)
bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)
bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)

この link にアクセスしてください。

編集:

誰かの質問について

なぜ、signed intが未定義でオーバーフローしていないのか、unsignedはオーバーフローしないのはなぜかを説明するのは、有益で有益だと思います。

答えはコンパイラの実装に依存します。ほとんどのC実装(コンパイラ)は、使用する整数表現で実装するのが最も簡単なオーバーフロー動作を使用しました。

実際には、(実装に応じて)符号付き値の表現は異なる場合があります。one's complementtwo's complementsign-magnitude。符号なしの型の場合、_ [binary representation(標準ではバイナリ表現のみが許可されています)。

20
abhiarora

検出符号付きintオーバーフローはできません。 avoid itにコードを記述する必要があります。

符号付きintオーバーフローは未定義の動作であり、プログラムに存在する場合、プログラムは無効であり、コンパイラーは特定の動作を生成する必要はありません。

30
Jesper Juhl

符号付きオペランドは、加算を実行する前にテストする必要があります。以下は、すべてのケースで2つの比較を行う安全な加算関数です。

#include <limits.h>

int safe_add(int a, int b) {
    if (a >= 0) {
        if (b > INT_MAX - a) {
            /* handle overflow */
        } else {
            return a + b;
        }
    } else {
        if (b < INT_MIN - a) {
            /* handle negative overflow */
        } else {
            return a + b;
        }
    }
}

タイプlong longは、タイプintよりも範囲が広いことがわかっています。このアプローチを使用すると、より速く証明される可能性があります。

#include <limits.h>

int safe_add(int a, int b) {
    long long res = (long long)a + b;
    if (res > INT_MAX || res < INT_MIN) {
        /* handle overflow */
    } else {
        return (int)res;
    }
}