696

a b = cのすべての解を見つけるために C++ でプログラムを作成していました。ここで、 ab、およびcは一緒にすべての数字 0-9 を 1 回だけ使用します。プログラムはabの値をループし、 aba bで毎回桁カウント ルーチンを実行して、桁数の条件が満たされているかどうかを確認しました。

ただし、 a bが整数の制限を超えると、偽の解が生成される可能性があります。私は次のようなコードを使用してこれをチェックしました:

unsigned long b, c, c_test;
...
c_test=c*b;         // Possible overflow
if (c_test/b != c) {/* There has been an overflow*/}
else c=c_test;      // No overflow

オーバーフローをテストするより良い方法はありますか? 一部のチップには、オーバーフローが発生したときに設定される内部フラグがあることは知っていますが、C または C++ を介してアクセスされるのを見たことがありません。


符号付き intオーバーフローは C および C++ では未定義の動作であるため、実際に発生させずに検出する必要があることに注意してください。加算前の signed int オーバーフローについては、C/C++ での符号付きオーバーフローの検出を参照してください。

4

31 に答える 31

275

符号なし整数を使用しているようです。定義上、C (私は C++ については知りません) では、符号なし算術演算はオーバーフローしません ... したがって、少なくとも C の場合、あなたの主張は意味がありません :)

符号付き整数では、オーバーフローが発生すると、未定義の動作(UB) が発生し、プログラムは何でもできます (たとえば、レンダリング テストが決定的でない)。 

#include <limits.h>

int a = <something>;
int x = <something>;
a += x;              /* UB */
if (a < 0) {         /* Unreliable test */
  /* ... */
}

適合するプログラムを作成するには、オーバーフローを生成する前にオーバーフローをテストする必要があります。メソッドは符号なし整数でも使用できます。

// For addition
#include <limits.h>

int a = <something>;
int x = <something>;
if ((x > 0) && (a > INT_MAX - x)) /* `a + x` would overflow */;
if ((x < 0) && (a < INT_MIN - x)) /* `a + x` would underflow */;

// For subtraction
#include <limits.h>
int a = <something>;
int x = <something>;
if ((x < 0) && (a > INT_MAX + x)) /* `a - x` would overflow */;
if ((x > 0) && (a < INT_MIN + x)) /* `a - x` would underflow */;

// For multiplication
#include <limits.h>

int a = <something>;
int x = <something>;
// There may be a need to check for -1 for two's complement machines.
// If one number is -1 and another is INT_MIN, multiplying them we get abs(INT_MIN) which is 1 higher than INT_MAX
if ((a == -1) && (x == INT_MIN)) /* `a * x` can overflow */
if ((x == -1) && (a == INT_MIN)) /* `a * x` (or `a / x`) can overflow */
// general case
if (a > INT_MAX / x) /* `a * x` would overflow */;
if ((a < INT_MIN / x)) /* `a * x` would underflow */;

INT_MIN除算の場合 ( andの特殊な場合を除いて)、 or-1を超える可能性はありません。INT_MININT_MAX

于 2009-10-03T17:15:16.160 に答える
167

演算がオーバーフローする可能性あるかどうかを判断するには、オペランドの最上位 1 ビットの位置と基本的な 2 進演算の知識を使用する方法があります。

加算の場合、任意の 2 つのオペランドは、最大のオペランドの最上位の 1 ビットよりも (最大で) 1 ビット多くなります。例えば:

bool addition_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
    return (a_bits<32 && b_bits<32);
}

乗算の場合、任意の 2 つのオペランドは (最大でも) オペランドのビットの合計になります。例えば:

bool multiplication_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
    return (a_bits+b_bits<=32);
}

同様に、次のようaに のべき乗の結果の最大サイズを見積もることができます。b

bool exponentiation_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a);
    return (a_bits*b<=32);
}

(もちろん、ターゲット整数のビット数を置き換えてください。)

数値の最上位の 1 ビットの位置を決定する最速の方法がわかりません。ここに力ずくの方法があります。

size_t highestOneBitPosition(uint32_t a) {
    size_t bits=0;
    while (a!=0) {
        ++bits;
        a>>=1;
    };
    return bits;
}

これは完全ではありませんが、操作を行う前に、任意の 2 つの数値がオーバーフローする可能性があるかどうかを判断するのに役立ちます。関数のループのために、提案した方法で結果を単にチェックするよりも速いかどうかはわかりませんが、highestOneBitPositionそうなる可能性があります(特に、オペランドに含まれるビット数が事前にわかっている場合)。

于 2008-10-13T23:44:49.590 に答える
58

一部のコンパイラは、CPU の整数オーバーフロー フラグへのアクセスを提供しており、これをテストできますが、これは標準ではありません。

乗算を実行する前に、オーバーフローの可能性をテストすることもできます。

if ( b > ULONG_MAX / a ) // a * b would overflow
于 2008-10-13T23:02:46.117 に答える
43

警告:GCCは、でコンパイルするときにオーバーフローチェックを最適化できます-O2。このオプション-Wallは、次のような場合に警告を表示します

if (a + b < a) { /* Deal with overflow */ }

ただし、この例ではありません。

b = abs(a);
if (b < 0) { /* Deal with overflow */ }

CERTの論文で説明されているように、オーバーフローが発生する前にオーバーフローをチェックするのが唯一の安全な方法であり、これを体系的に使用するのは非常に面倒です。

でコンパイル-fwrapvすると問題は解決しますが、一部の最適化は無効になります。

より良い解決策が切実に必要です。オーバーフローが発生しないことに依存する最適化を行う場合、コンパイラはデフォルトで警告を発行する必要があると思います。現在の状況では、コンパイラーはオーバーフローチェックを最適化することができますが、これは私の意見では受け入れられません。

于 2011-07-25T21:40:17.537 に答える
34

Clangは、符号付き整数と符号なし整数の両方の動的オーバーフローチェックをサポートするようになりました。-fsanitize=integerスイッチを参照してください。今のところ、デバッグ目的で完全にサポートされている動的オーバーフローチェックを備えた唯一のC++コンパイラです。

于 2013-01-28T17:51:30.800 に答える
28

多くの人がオーバーフローに関する質問に答えているようですが、私は彼の元の問題に対処したかったのです。彼は、問題は、すべての数字が繰り返されずに使用されるようにa b =cを見つけることだと言いました。わかりました、それは彼がこの投稿で尋ねたことではありませんが、問題の上限を研究し、オーバーフローを計算または検出する必要はないと結論付ける必要があったと私はまだ考えています (注: 私は熟練していません)数学ではこれを段階的に行いましたが、最終結果は非常に単純だったので、これは単純な式になる可能性があります)。

要点は、問題が a、b、または c のいずれかに対して必要とする上限が 98.765.432 であるということです。とにかく、問題を些細な部分とそうでない部分に分割することから始めます。

  • x 0 == 1 (9、8、7、6、5、4、3、2 の順列はすべて解)
  • x 1 == x (解はありません)
  • 0 b == 0 (解決不可能)
  • 1 b == 1 (解決不可能)
  • a b、a > 1、b > 1 (非自明)

ここで必要なのは、他の解決策はなく、順列のみが有効であることを示すことだけです (そして、それらを出力するコードは自明です)。上限に戻ります。実際の上限は c ≤ 98.765.432 です。これは 8 桁の最大数であるため、上限です (合計 10 桁から a と b のそれぞれに対して 1 を引いた数)。この上限は c のみです。これは、b を 2 から上限まで変化させて計算できるように、指数関数的な成長のため、a と b の境界ははるかに低くなければならないからです。

    9938.08^2 == 98765432
    462.241^3 == 98765432
    99.6899^4 == 98765432
    39.7119^5 == 98765432
    21.4998^6 == 98765432
    13.8703^7 == 98765432
    9.98448^8 == 98765432
    7.73196^9 == 98765432
    6.30174^10 == 98765432
    5.33068^11 == 98765432
    4.63679^12 == 98765432
    4.12069^13 == 98765432
    3.72429^14 == 98765432
    3.41172^15 == 98765432
    3.15982^16 == 98765432
    2.95305^17 == 98765432
    2.78064^18 == 98765432
    2.63493^19 == 98765432
    2.51033^20 == 98765432
    2.40268^21 == 98765432
    2.30883^22 == 98765432
    2.22634^23 == 98765432
    2.15332^24 == 98765432
    2.08826^25 == 98765432
    2.02995^26 == 98765432
    1.97741^27 == 98765432

たとえば、最後の行に注意してください: 1.97^27 ~98M と書かれています。したがって、たとえば、 1^27 == 1 および 2^27 == 134.217.728 は、9 桁 (2 > 1.97 であるため、実際にはテスト対象よりも大きい) であるため、解決策ではありません。ご覧のとおり、a と b のテストに使用できる組み合わせは非常に少ないです。b == 14 の場合、2 と 3 を試す必要があります。b == 3 の場合、2 で開始し、462 で停止します。すべての結果は ~98M 未満であることが認められます。

上記のすべての組み合わせをテストして、数字を繰り返さない組み合わせを探します。

    ['0', '2', '4', '5', '6', '7', '8'] 84^2 = 7056
    ['1', '2', '3', '4', '5', '8', '9'] 59^2 = 3481
    ['0', '1', '2', '3', '4', '5', '8', '9'] 59^2 = 3481 (+leading zero)
    ['1', '2', '3', '5', '8'] 8^3 = 512
    ['0', '1', '2', '3', '5', '8'] 8^3 = 512 (+leading zero)
    ['1', '2', '4', '6'] 4^2 = 16
    ['0', '1', '2', '4', '6'] 4^2 = 16 (+leading zero)
    ['1', '2', '4', '6'] 2^4 = 16
    ['0', '1', '2', '4', '6'] 2^4 = 16 (+leading zero)
    ['1', '2', '8', '9'] 9^2 = 81
    ['0', '1', '2', '8', '9'] 9^2 = 81 (+leading zero)
    ['1', '3', '4', '8'] 3^4 = 81
    ['0', '1', '3', '4', '8'] 3^4 = 81 (+leading zero)
    ['2', '3', '6', '7', '9'] 3^6 = 729
    ['0', '2', '3', '6', '7', '9'] 3^6 = 729 (+leading zero)
    ['2', '3', '8'] 2^3 = 8
    ['0', '2', '3', '8'] 2^3 = 8 (+leading zero)
    ['2', '3', '9'] 3^2 = 9
    ['0', '2', '3', '9'] 3^2 = 9 (+leading zero)
    ['2', '4', '6', '8'] 8^2 = 64
    ['0', '2', '4', '6', '8'] 8^2 = 64 (+leading zero)
    ['2', '4', '7', '9'] 7^2 = 49
    ['0', '2', '4', '7', '9'] 7^2 = 49 (+leading zero)

それらのどれも問題に一致しません (これは、'0'、'1'、...、'9' がないことでもわかります)。

それを解決するサンプルコードは次のとおりです。また、これは Python で書かれていることに注意してください。これは、任意精度の整数が必要だからではなく (コードは 9800 万を超えるものは計算しません)、テストの量が非常に少ないため、高水準言語を使用する必要があることがわかったためです。組み込みのコンテナーとライブラリーを利用します (コードには 28 行あります)。

    import math

    m = 98765432
    l = []
    for i in xrange(2, 98765432):
        inv = 1.0/i
        r = m**inv
        if (r < 2.0): break
        top = int(math.floor(r))
        assert(top <= m)

        for j in xrange(2, top+1):
            s = str(i) + str(j) + str(j**i)
            l.append((sorted(s), i, j, j**i))
            assert(j**i <= m)

    l.sort()
    for s, i, j, ji in l:
        assert(ji <= m)
        ss = sorted(set(s))
        if s == ss:
            print '%s %d^%d = %d' % (s, i, j, ji)

        # Try with non significant zero somewhere
        s = ['0'] + s
        ss = sorted(set(s))
        if s == ss:
            print '%s %d^%d = %d (+leading zero)' % (s, i, j, ji)
于 2013-03-11T01:57:31.330 に答える
25

これは、少なくとも加算のオーバーフローを検出するための非常に高速な方法です。これにより、乗算、除算、べき乗が発生する可能性があります。

アイデアは、プロセッサが値をゼロに戻すだけで、C/C++ が特定のプロセッサから抽象化されるため、次のことができるということです。

uint32_t x, y;
uint32_t value = x + y;
bool overflow = value < (x | y);

これにより、オペランドの 1 つが 0 で 1 つがそうでない場合に、オーバーフローが誤って検出されず、以前に提案された多くの NOT/XOR/AND/テスト操作よりも大幅に高速になります。

指摘したように、このアプローチは他のより精巧な方法よりも優れていますが、それでも最適化可能です。以下は、最適化を含む元のコードのリビジョンです。

uint32_t x, y;
uint32_t value = x + y;
const bool overflow = value < x; // Alternatively "value < y" should also work

乗算オーバーフローを検出するより効率的で安価な方法は次のとおりです。

uint32_t x, y;
const uint32_t a = (x >> 16U) * (y & 0xFFFFU);
const uint32_t b = (x & 0xFFFFU) * (y >> 16U);
const bool overflow = ((x >> 16U) * (y >> 16U)) +
    (a >> 16U) + (b >> 16U);
uint32_t value = overflow ? UINT32_MAX : x * y;

これにより、オーバーフローの UINT32_MAX または乗算の結果が発生します。この場合、符号付き整数に対して乗算を続行できるようにすることは、厳密には未定義の動作です。

注目すべきは、これは、部分カラツバ法乗算分解を使用して、64 ビット乗算の上位 32 ビットを計算し、32 ビット乗算がオーバーフローするかどうかを知るためにそれらのいずれかを設定する必要があるかどうかをチェックすることです。

C++ を使用している場合は、これをきちんとした小さなラムダに変換してオーバーフローを計算し、検出器の内部動作を非表示にすることができます。

uint32_t x, y;
const bool overflow
{
    [](const uint32_t x, const uint32_t y) noexcept -> bool
    {
        const uint32_t a{(x >> 16U) * uint16_t(y)};
        const uint32_t b{uint16_t(x) * (y >> 16U)};
        return ((x >> 16U) * (y >> 16U)) + (a >> 16U) + (b >> 16U);
    }(x, y)
};
uint32_t value{overflow ? UINT32_MAX : x * y};
于 2011-06-24T19:44:22.500 に答える
23

これは、質問に対する「移植性のない」ソリューションです。Intel x86 および x64 CPU には、いわゆるEFLAGS-registerがあり、各整数算術演算の後にプロセッサによって埋められます。ここでは詳細な説明を省略します。関連するフラグは、「オーバーフロー」フラグ (マスク 0x800) と「キャリー」フラグ (マスク 0x1) です。それらを正しく解釈するには、オペランドが符号付きか符号なしかを考慮する必要があります。

C/C++ からフラグをチェックする実用的な方法を次に示します。次のコードは、Visual Studio 2005以降 (32 ビットと 64 ビットの両方)、および GNU C/C++ 64 ビットで動作します。

#include <cstddef>
#if defined( _MSC_VER )
#include <intrin.h>
#endif

inline size_t query_intel_x86_eflags(const size_t query_bit_mask)
{
    #if defined( _MSC_VER )

        return __readeflags() & query_bit_mask;

    #elif defined( __GNUC__ )
        // This code will work only on 64-bit GNU-C machines.
        // Tested and does NOT work with Intel C++ 10.1!
        size_t eflags;
        __asm__ __volatile__(
            "pushfq \n\t"
            "pop %%rax\n\t"
            "movq %%rax, %0\n\t"
            :"=r"(eflags)
            :
            :"%rax"
            );
        return eflags & query_bit_mask;

    #else

        #pragma message("No inline assembly will work with this compiler!")
            return 0;
    #endif
}

int main(int argc, char **argv)
{
    int x = 1000000000;
    int y = 20000;
    int z = x * y;
    int f = query_intel_x86_eflags(0x801);
    printf("%X\n", f);
}

オペランドがオーバーフローなしで乗算された場合、 からの戻り値は 0 になります。query_intel_eflags(0x801)つまり、キャリー フラグもオーバーフロー フラグも設定されません。提供されている main() のコード例では、オーバーフローが発生し、両方のフラグが 1 に設定されています。このチェックはそれ以上の計算を意味するものではないため、非常に高速です。

于 2012-12-07T13:48:39.527 に答える
22

テストしたいデータ型よりも大きいデータ型がある場合 (32 ビットの加算を行い、64 ビットの型があるとします)、オーバーフローが発生したかどうかを検出します。私の例は、8 ビット加算です。しかし、それはスケールアップすることができます。

uint8_t x, y;    /* Give these values */
const uint16_t data16    = x + y;
const bool carry        = (data16 > 0xFF);
const bool overflow     = ((~(x ^ y)) & (x ^ data16) & 0x80);

このページで説明されている概念に基づいています: http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Comb/overflow.html

32 ビットの例では、 に0xFFなり0xFFFFFFFF、に0x80なり0x80000000、最終的に にuint16_tなりますuint64_t

:これは整数の加算/減算のオーバーフローをキャッチします。あなたの質問には乗算が含まれていることに気付きました。その場合、除算がおそらく最良のアプローチです。これは一般にcalloc、最終的なサイズを取得するために乗算されるときにパラメーターがオーバーフローしないようにする実装の方法です。

于 2008-10-14T01:05:15.293 に答える
18

最も簡単な方法は、unsigned longs をunsigned long longs に変換し、乗算を行い、結果を 0x100000000LL と比較することです。

これは、例で行ったように除算を行うよりも効率的であることがおそらくわかるでしょう。

ああ、C と C++ の両方で動作します (質問に両方のタグを付けているため)。


glibc manualを見ているだけです。の一部として、整数オーバーフロー トラップ ( FPE_INTOVF_TRAP)についての言及がありSIGFPEます。マニュアルの厄介な部分を除けば、それは理想的です。

FPE_INTOVF_TRAP 整数オーバーフロー (ハードウェア固有の方法でオーバーフロー トラップを有効にしない限り、C プログラムでは不可能)。

本当に少し残念です。

于 2008-10-13T22:59:20.337 に答える
15

C/C++ からオーバーフロー フラグにアクセスすることはできません。

一部のコンパイラでは、トラップ命令をコードに挿入できます。GCC では、オプションは-ftrapv.

移植可能でコンパイラに依存しない唯一の方法は、自分でオーバーフローをチェックすることです。あなたの例でやったように。

ただし、-ftrapv最新の GCC を使用した x86 では何もしないようです。古いバージョンの残り物か、他のアーキテクチャに固有のものだと思います。各追加の後にコンパイラが INTO オペコードを挿入することを期待していました。残念ながら、これは行いません。

于 2008-10-13T22:59:37.947 に答える
13

符号なし整数の場合は、結果が引数の1つよりも小さいことを確認してください。

unsigned int r, a, b;
r = a + b;
if (r < a)
{
    // Overflow
}

符号付き整数の場合、引数と結果の符号を確認できます。

異なる符号の整数はオーバーフローできません。同じ符号の整数は、結果が異なる符号の場合にのみオーバーフローします。

signed int r, a, b, s;
r = a + b;
s = a>=0;
if (s == (b>=0) && s != (r>=0))
{
    // Overflow
}
于 2009-02-09T14:06:39.337 に答える
11

浮動小数点数についても同じ質問に答える必要がありました。浮動小数点数では、ビット マスキングとシフトが有望に見えません。私が決めたアプローチは、符号付きおよび符号なしの整数および浮動小数点数に対して機能します。中間計算のために昇格する大きなデータ型がない場合でも機能します。これらすべてのタイプで最も効率的というわけではありませんが、すべてのタイプで機能するため、使用する価値があります。

符号付きオーバーフロー テスト、加算と減算:

  1. 型の最大値と最小値を表す定数、MAXVALUE および MINVALUE を取得します。

  2. オペランドの符号を計算して比較します。

    を。いずれかの値がゼロの場合、加算も減算もオーバーフローできません。残りのテストをスキップします。

    b. 符号が反対の場合、足し算はオーバーフローできません。残りのテストをスキップします。

    c. 符号が同じ場合、減算はオーバーフローできません。残りのテストをスキップします。

  3. MAXVALUE の正のオーバーフローをテストします。

    を。両方の符号が正で、MAXVALUE - A < B の場合、加算はオーバーフローします。

    b. B の符号が負で、MAXVALUE - A < -B の場合、減算はオーバーフローします。

  4. MINVALUE の負のオーバーフローをテストします。

    を。両方の符号が負で、MINVALUE - A > B の場合、加算はオーバーフローします。

    b. A の符号が負で、MINVALUE - A > B の場合、減算はオーバーフローします。

  5. それ以外の場合、オーバーフローはありません。

符号付きオーバーフロー テスト、乗算と除算:

  1. 型の最大値と最小値を表す定数、MAXVALUE および MINVALUE を取得します。

  2. オペランドの大きさ (絶対値) を計算して 1 と比較します。(以下では、A と B が署名されたオリジナルではなく、これらの大きさであると仮定します。)

    を。いずれかの値がゼロの場合、乗算はオーバーフローできず、除算はゼロまたは無限大になります。

    b. いずれかの値が 1 の場合、乗算と除算はオーバーフローできません。

    c. 1 つのオペランドの大きさが 1 より小さく、もう 1 つのオペランドの大きさが 1 より大きい場合、乗算はオーバーフローできません。

    d. 大きさが両方とも 1 未満の場合、除算はオーバーフローできません。

  3. MAXVALUE の正のオーバーフローをテストします。

    を。両方のオペランドが 1 より大きく、MAXVALUE / A < B の場合、乗算はオーバーフローします。

    b. B が 1 未満で MAXVALUE * B < A の場合、除算はオーバーフローします。

  4. それ以外の場合、オーバーフローはありません。

注: 絶対値を使用したため、MINVALUE の最小オーバーフローは 3 で処理されます。ただし、ABS(MINVALUE) > MAXVALUE の場合、まれに誤検知が発生します。

アンダーフローのテストも同様ですが、EPSILON (ゼロより大きい正の最小数) が含まれます。

于 2012-05-21T14:53:50.043 に答える
8

もう1つの興味深いツールは、IOC:C /C++用の整数オーバーフローチェッカーです

これはパッチが適用されたClangコンパイラであり、コンパイル時にコードにチェックを追加します。

次のような出力が得られます。

CLANG ARITHMETIC UNDEFINED at <add.c, (9:11)> :
Op: +, Reason : Signed Addition Overflow,
BINARY OPERATION: left (int32): 2147483647 right (int32): 1
于 2012-10-04T12:08:10.140 に答える
8

CERT は、"as-if" 無限範囲 (AIR) 整数モデルを使用して、符号付き整数のオーバーフロー、符号なし整数のラッピング、および整数の切り捨てを検出して報告する新しいアプローチを開発しました。CERT は、モデルを説明するテクニカル レポートを発行し、GCC 4.4.0 および GCC 4.5.0 に基づいて実用的なプロトタイプを作成しました。

AIR 整数モデルは、無限の範囲の整数を使用して取得された値と同等の値を生成するか、ランタイム制約違反になります。以前の整数モデルとは異なり、AIR 整数は正確なトラップを必要としないため、既存のほとんどの最適化を中断したり抑制したりしません。

于 2009-10-03T16:46:47.840 に答える
5

double で結果を計算します。有効数字は 15 桁です。あなたの要件では、 cに 10 8の厳密な上限が あります。最大 8 桁です。したがって、範囲内であれば結果は正確になり、それ以外の場合はオーバーフローしません。

于 2008-10-14T07:30:00.787 に答える
2

Cで整数のオーバーフローをキャッチすると、GCC拡張機能が必要な場合でも(処理されるタイプに関してはより一般的です)、CERTで説明されているソリューションよりも一般的なソリューションが示されます(それらがどれほど広くサポートされているかはわかりません)。

于 2010-05-01T18:36:57.523 に答える
2

C/C++ からオーバーフロー フラグにアクセスすることはできません。

私はこれに同意しません。x86を使用していると仮定して、インラインアセンブリ言語を記述し、jo(ジャンプオーバーフロー)命令を使用してオーバーフローをトラップできます。もちろん、あなたのコードは他のアーキテクチャに移植できなくなります。

と を見てinfo asくださいinfo gcc

于 2008-10-13T23:25:45.290 に答える
0

Head Geekの答えを拡張するには、より高速な方法がありaddition_is_safeます。

bool addition_is_safe(unsigned int a, unsigned int b)
{
    unsigned int L_Mask = std::numeric_limits<unsigned int>::max();
    L_Mask >>= 1;
    L_Mask = ~L_Mask;

    a &= L_Mask;
    b &= L_Mask;

    return ( a == 0 || b == 0 );
}

これは、64 ビットおよび 32 ビットの符号なし整数が正常に機能するという点で、マシン アーキテクチャ セーフを使用します。基本的に、最上位ビット以外をマスクするマスクを作成します。次に、両方の整数をマスクします。いずれかのビットが設定されていなければ、加算は安全です。

コンストラクターでマスクを事前に初期化すると、マスクは変更されないため、これはさらに高速になります。

于 2013-02-13T17:34:18.263 に答える
0

x86 命令セットには、結果を 2 つのレジスタに格納する符号なし乗算命令が含まれています。C からその命令を使用するには、64 ビット プログラム (GCC) で次のコードを記述できます。

unsigned long checked_imul(unsigned long a, unsigned long b) {
  unsigned __int128 res = (unsigned __int128)a * b;
  if ((unsigned long)(res >> 64))
    printf("overflow in integer multiply");
  return (unsigned long)res;
}

32 ビット プログラムの場合、結果を 64 ビットにし、パラメータを 32 ビットにする必要があります。

別の方法として、コンパイラ依存の組み込み関数を使用してフラグ レジスタをチェックする方法があります。オーバーフロー組み込みの GCC ドキュメントは、 6.56 Built-in Functions to Perform Arithmetic with Overflow Checkingから見つけることができます。

于 2015-11-18T19:29:54.390 に答える
-1

MSalter の答えは良い考えです。

(精度のために)整数計算が必要であるが、浮動小数点が利用可能な場合は、次のようなことができます。

uint64_t foo(uint64_t a, uint64_t b) {
    double dc;

    dc = pow(a, b);

    if (dc < UINT_MAX) {
       return (powu64(a, b));
    }
    else {
      // Overflow
    }
}
于 2008-10-14T18:43:06.670 に答える
-2

これを行うクリーンな方法は、すべての演算子 (特に + と *) をオーバーライドし、操作を実行する前にオーバーフローをチェックすることです。

于 2008-10-13T23:07:19.960 に答える
-3

移植可能な方法でオーバーフローせずに符号なし乗算を実行するには、次を使用できます。

... /* begin multiplication */
unsigned multiplicand, multiplier, product, productHalf;
int zeroesMultiplicand, zeroesMultiplier;
zeroesMultiplicand = number_of_leading_zeroes( multiplicand );
zeroesMultiplier   = number_of_leading_zeroes( multiplier );
if( zeroesMultiplicand + zeroesMultiplier <= 30 ) goto overflow;
productHalf = multiplicand * ( c >> 1 );
if( (int)productHalf < 0 ) goto overflow;
product = productHalf * 2;
if( multiplier & 1 ){
   product += multiplicand;
   if( product < multiplicand ) goto overflow;
}
..../* continue code here where "product" is the correct product */
....
overflow: /* put overflow handling code here */

int number_of_leading_zeroes( unsigned value ){
   int ctZeroes;
   if( value == 0 ) return 32;
   ctZeroes = 1;
   if( ( value >> 16 ) == 0 ){ ctZeroes += 16; value = value << 16; }
   if( ( value >> 24 ) == 0 ){ ctZeroes +=  8; value = value <<  8; }
   if( ( value >> 28 ) == 0 ){ ctZeroes +=  4; value = value <<  4; }
   if( ( value >> 30 ) == 0 ){ ctZeroes +=  2; value = value <<  2; }
   ctZeroes -= x >> 31;
   return ctZeroes;
}
于 2015-01-21T21:28:11.173 に答える
-3

何に使うかによります。unsigned long (DWORD) 加算または乗算を実行する場合、最適なソリューションは ULARGE_INTEGER を使用することです。

ULARGE_INTEGER は、2 つの DWORD の構造です。完全な値は「QuadPart」としてアクセスできますが、上位の DWORD は「HighPart」としてアクセスされ、下位の DWORD は「LowPart」としてアクセスされます。

例えば:

DWORD
My Addition(DWORD Value_A, DWORD Value_B)
{
    ULARGE_INTEGER a, b;

    b.LowPart = Value_A;  // A 32 bit value(up to 32 bit)
    b.HighPart = 0;
    a.LowPart = Value_B;  // A 32 bit value(up to 32 bit)
    a.HighPart = 0;

    a.QuadPart += b.QuadPart;

    // If  a.HighPart
    // Then a.HighPart contains the overflow (carry)

    return (a.LowPart + a.HighPart)

    // Any overflow is stored in a.HighPart (up to 32 bits)
于 2013-10-03T23:43:50.720 に答える
-3
#include <stdio.h>
#include <stdlib.h>

#define MAX 100 

int mltovf(int a, int b)
{
    if (a && b) return abs(a) > MAX/abs(b);
    else return 0;
}

main()
{
    int a, b;

    for (a = 0; a <= MAX; a++)
        for (b = 0; b < MAX; b++) {

        if (mltovf(a, b) != (a*b > MAX)) 
            printf("Bad calculation: a: %d b: %d\n", a, b);

    }
}
于 2014-06-20T19:22:43.877 に答える
-4

オーバーフローをテストする簡単な方法は、現在の値が前の値よりも小さいかどうかをチェックして検証を行うことです。たとえば、2の累乗を出力するループがあったとします。

long lng;
int n;
for (n = 0; n < 34; ++n)
{
   lng = pow (2, n);
   printf ("%li\n", lng);
}

私が説明した方法でオーバーフローチェックを追加すると、次のようになります。

long signed lng, lng_prev = 0;
int n;
for (n = 0; n < 34; ++n)
{
    lng = pow (2, n);
    if (lng <= lng_prev)
    {
        printf ("Overflow: %i\n", n);
        /* Do whatever you do in the event of overflow.  */
    }
    printf ("%li\n", lng);
    lng_prev = lng;
}

符号なしの値だけでなく、正と負の両方の符号付きの値に対しても機能します。

もちろん、値を増やすのではなく、値を減らすために同様のことをしたい場合は、アンダーフローの動作がオーバーフローの動作と同じであると仮定して、<=符号を反転してそれを作成します。>=正直なところ、これはCPUのオーバーフローフラグにアクセスせずに取得できるのとほぼ同じくらい移植性があります(インラインアセンブリコードが必要になるため、実装間でコードを移植できなくなります)。

于 2010-05-01T19:01:54.123 に答える
-6

インライン アセンブリを使用すると、オーバーフロー ビットを直接確認できます。C++ を使用する場合は、アセンブリを学習する必要があります。

于 2008-10-13T22:59:11.817 に答える