web-dev-qa-db-ja.com

符号なし整数乗算のオーバーフローを検出する方法を教えてください。

aのすべての解を見つけるためにC++でプログラムを書いていましたb = 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++を通してアクセスされるのを見たことがありません。


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

567
Chris Johnson

符号なし整数を使用しているようです。定義上、in C(C++については知りません)、符号なし算術演算子がオーバーフローすることはありません。

符号付き整数では、オーバーフローが発生すると 未定義の動作 が発生し、プログラムは何でもできるようになります(たとえば、テストの確定に未確定)。

#include <limits.h>
int a = <something>;
int x = <something>;
a += x;              /* UB */
if (a < 0) {         /* unreliable test */
  /* ... */
}

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

// 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 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および-1特殊な場合を除く)、INT_MINまたはINT_MAXを超える可能性はありません。

193
pmg

is オペランドの最上位の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関数のループのために、単にあなたが提案した方法で結果をチェックするよりも速いかどうかはわかりませんが、それはおそらく可能です(特に、オペランドのビット数があらかじめわかっている場合)。

162
Head Geek

Clang 3.4+ および GCC 5+ は算術算術組み込み関数を提供します。特にビットテストによる安全性チェックと比較すると、これらの問題は非常に高速な解決策を提供します。

OPの質問の例では、それはそのように動作します:

unsigned long b, c, c_test;
if (__builtin_umull_overflow(b, c, &c_test))
{
    // returned non-zero: there has been an overflow
}
else
{
    // return zero: there hasn't been an overflow
}

Clangのドキュメントでは、オーバーフローが発生した場合にc_testにオーバーフローした結果が含まれるかどうかは指定されていませんが、GCCのドキュメントには含まれています。これら2つが__builtin互換であることを好むことを考えれば、これがClangも機能する方法であると仮定することはおそらく安全です。

Intサイズ、longサイズ、およびlong longサイズの場合、符号付きおよび符号なしのバリアントでオーバーフロー(加算、減算、乗算)する可能性がある算術演算ごとに__builtinがあります。名前の構文は__builtin_[us](operation)(l?l?)_overflowです。

  • unsigned の場合はu signed の場合はs
  • 操作はaddsubまたはmulのいずれかです。
  • l接尾辞がないということは、オペランドがintであることを意味します。 1つのllongを意味します。 2つのllong longを意味します。

そのため、チェック付きの符号付き長整数型加算の場合、__builtin_saddl_overflowになります。完全なリストは Clangドキュメントページ にあります。

GCC 5以降およびClang 3.8以降では、値の型を指定せずに機能する汎用の組み込み関数(__builtin_add_overflow__builtin_sub_overflow、および__builtin_mul_overflow)も提供されています。これらはintより小さい型でも動作します。

ビルトインはプラットフォームに最適なものより低くなります。 x86では、それらはキャリー、オーバーフロー、そしてサインフラグをチェックします。

Visual Studioのcl.exeには、直接同等のものはありません。 <intrin.h>を含む符号なしの加算および減算では、addcarry_uNNおよびsubborrow_uNNを使用できます(NNはaddcarry_u8またはsubborrow_u64のようにビット数です)。彼らの署名は少し曖昧です:

unsigned char _addcarry_u32(unsigned char c_in, unsigned int src1, unsigned int src2, unsigned int *sum);
unsigned char _subborrow_u32(unsigned char b_in, unsigned int src1, unsigned int src2, unsigned int *diff);

c_in/b_inは入力時のキャリー/ボローフラグ、戻り値は出力時のキャリー/ボローフです。符号付き演算や乗算と同等のものはないようです。

そうでなければ、Clang for Windowsは現在プロダクションに対応しています(Chromeには十分です)ので、それも選択肢の可能性があります。

130
zneak

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

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

if ( b > ULONG_MAX / a ) // a * b would overflow
51
Robert Gamble

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

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

しかし、この例ではそうではありません。

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

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

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

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

38
A Fog

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

30
ZAB

これは、質問に対する「移植性のない」解決策です。 Intel x86およびx64 CPUにはいわゆるEFLAGSレジスタ( http://en.wikipedia.org/wiki/EFLAGS )があり、各整数算術演算の後にプロセッサによって入力されます。ここでは詳細な説明は省きます。関連フラグは、「オーバーフロー」フラグ(マスク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 );
}

オペランドがオーバーフローせずに乗算された場合、query_intel_eflags(0x801)から戻り値0が返されます。つまり、キャリーフラグもオーバーフローフラグも設定されません。提供されているmain()のサンプルコードでは、オーバーフローが発生し、両方のフラグが1に設定されています。

23

多くの人がオーバーフローについての質問に答えたようですが、私は彼の最初の問題に対処したいと思いました。彼は問題は発見することであると言いましたb= cすべての数字が繰り返されることなく使用されるようにします。オーケー、それは彼がこの記事で尋ねたものではありません、しかし私はまだ問題の上限を研究し、彼が決してオーバーフローを計算したり検出する必要はないと結論づける必要があると思いました。数学ではそう私は一歩一歩このステップをしたが、最終結果はこれが簡単な公式を持っているかもしれないほど簡単だった。

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

  • バツ == 1(9、8、7、6、5、4、3、2のすべての置換が解です)
  • バツ1 == x(解決策はありません)
  • b == 0(解決策はありません)
  • 1b == 1(解決策はありません)
  • ある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で書かれていることに注意してください。任意の精度の整数が必要なわけではないので(コードは9,900万を超えるものを計算しない)、テストの量が少ないので高水準言語を使用する必要があるその組み込みのコンテナとライブラリを利用してください(また、コードには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)
23
hdante

あなたがテストしたいものよりも大きいデータ型を持っているなら(あなたが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ビットの例では、0xff0xffffffffになり、0x800x80000000になり、最後にuint16_tuint64_tになります。

_ note _ :これは整数の加算/減算のオーバーフローをキャッチします、そして私はあなたの質問が乗算を含むことに気づきました。その場合は、除算が最善の方法です。これは一般的にcallocの実装が最終的なサイズを得るためにそれらが乗算されるときにparamsがオーバーフローしないことを確認する方法です。

19
Evan Teran

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

あなたは、これがあなたの例で行ったように分割をするよりももっと効率的であることをおそらくあなたは見つけるでしょう。

ああ、そしてそれはCとC++の両方でうまくいくでしょう(あなたが両方で質問にタグを付けたように)。


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

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

ちょっと残念。

18

2年が経ちましたが、少なくとも足し算のオーバーフローを検出するための非常に高速な方法として私のペニスワースを追加することをお勧めします。

その考えは、プロセッサが単に値をゼロに折り返すことを可能にし、C/C++が特定のプロセッサから抽象化されるという理由からです。

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

これは両方とも、1つのオペランドが0であり、1つがそうでない場合に、オーバーフローが誤って検出されることがなく、以前に示唆されたNOT/XOR/AND/test操作よりもはるかに高速です。

編集 :指摘したように、このアプローチは他のより複雑な方法よりも優れていますが、まだ最適化可能です。以下は、最適化を含む元のコードの改訂です。

uint32_t x, y;
uint32_t value = x + y;
bool overflow = value < x; // Alternatively "value < y" should also work
15
DX-MON

符号なし整数の場合は、結果が引数の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
}
14
anonymous

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

一部のコンパイラでは、コードにトラップ命令を挿入できます。 GCCでは、オプションは-ftrapvです(ただし、使用したことはないことを認める必要があります。投稿後に確認します)。

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

編集:

チェックしたばかり:-ftrapvは最新のGCCを使用してx86上では何もしないようです。それは古いバージョンから取り残されているか、他のアーキテクチャに固有のものであると思います。私はコンパイラがそれぞれの追加の後にINTO命令コードを挿入すると思っていました。残念ながらそれはしません。

13

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

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

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

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

    a。どちらかの値がゼロの場合、加算も減算もオーバーフローすることはありません。残りのテストをスキップします。

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

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

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

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

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

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

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

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

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

符号付きオーバーフローテスト、乗算および除算

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

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

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

    b。どちらかの値が1の場合、乗算と除算はオーバーフローできません。

    c。一方のオペランドの絶対値が一方よりも小さく、他方の絶対値が1より大きい場合、乗算はオーバーフローできません。

    d。大きさが両方とも1より小さい場合、除算はオーバーフローできません。

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

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

    b。 Bが1より小さく、MAXVALUE * B <Aの場合、除算はオーバーフローします。

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

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

アンダーフローのテストは似ていますが、EPSILON(0より大きい正の最小数)を含みます。

11
Paul Chernoch

もう一つの興味深いツール: http://embed.cs.utah.edu/ioc/

これはパッチを当てたclangコンパイラで、コンパイル時にコードにチェックを追加します。それで、あなたはこのように見える出力を得ます:

CLANG ARITHMETIC UNDEFINED at <add.c, (9:11)> :
Op: +, Reason : Signed Addition Overflow, 
BINARY OPERATION: left (int32): 2147483647 right (int32): 1
8

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

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

7

アセンブラを使用したソリューションのもう1つの変形は外部手続きです。 Linux x64でg ++とfasmを使った符号なし整数乗算の例です。

この手続きは、2つの符号なし整数引数(32ビット)を乗算します( 仕様 AMD64用/による(セクション3.2.3パラメータの受け渡し)

クラスがINTEGERの場合は、%rdi、%rsi、%rdx、%rcx、%r8、および%r9の順序で次に使用可能なレジスタが使用されます。

(私のコードではediとesiが登録されています))結果を返すか、オーバーフローが発生した場合は0を返します。

format ELF64

section '.text' executable 

public u_mul

u_mul:
  MOV eax, edi
  mul esi
  jnc u_mul_ret
  xor eax, eax
u_mul_ret:
ret

テスト:

extern "C" unsigned int u_mul(const unsigned int a, const unsigned int b);

int main() {
    printf("%u\n", u_mul(4000000000,2));//0
    printf("%u\n", u_mul(UINT_MAX/2,2));//ok
    return 0;
}

プログラムをasmオブジェクトファイルとリンクします。私のQt Creatorの場合は、.proファイルでLIBSに追加します。

7
bartolo-otrit

結果をdoubleで計算します。彼らは15有効数字を持っています。あなたの要求は c に10の強い上限を持っています8 - 最大8桁です。したがって、範囲内であれば結果は正確になり、そうでなければオーバーフローすることはありません。

5
MSalters

このマクロを試して、32ビットマシンのオーバーフロービットをテストしてください(Angel Sinigerskyのソリューションを採用)。

#define overflowflag(isOverflow){   \
size_t eflags;                      \
asm ("pushfl ;"                     \
     "pop %%eax"                    \
    : "=a" (eflags));               \
isOverflow = (eflags >> 11) & 1;}

そうでなければオーバーフロービットが上書きされていたので、私はそれをマクロとして定義しました。

その後は、上記のコードセグメントを使用した小さなアプリケーションです。

#include <cstddef>
#include <stdio.h>
#include <iostream>
#include <conio.h>
#if defined( _MSC_VER )
#include <intrin.h>
#include <oskit/x86>
#endif

using namespace std;

#define detectOverflow(isOverflow){     \
size_t eflags;                      \
asm ("pushfl ;"                     \
    "pop %%eax"                     \
    : "=a" (eflags));               \
isOverflow = (eflags >> 11) & 1;}

int main(int argc, char **argv) {

    bool endTest = false;
    bool isOverflow;

    do {
        cout << "Enter two intergers" << endl;
        int x = 0;
        int y = 0;
        cin.clear();
        cin >> x >> y;
        int z = x * y;
        detectOverflow(isOverflow)
        printf("\nThe result is: %d", z);
        if (!isOverflow) {
            std::cout << ": no overflow occured\n" << std::endl;
        } else {
            std::cout << ": overflow occured\n" << std::endl;
        }

        z = x * x * y;
        detectOverflow(isOverflow)
        printf("\nThe result is: %d", z);
        if (!isOverflow) {
            std::cout << ": no overflow ocurred\n" << std::endl;
        } else {
            std::cout << ": overflow occured\n" << std::endl;
        }

        cout << "Do you want to stop? (Enter \"y\" or \"Y)" << endl;

        char c = 0;

        do {
            c = getchar();
        } while ((c == '\n') && (c != EOF));

        if (c == 'y' || c == 'Y') {
            endTest = true;
        }

        do {
            c = getchar();
        } while ((c != '\n') && (c != EOF));

    } while (!endTest);
}
5

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

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

info asinfo gccを見てください。

3
Tarski

Cで整数オーバーフローをキャッチ いくつかのGCC拡張が必要な​​場合でも、CERTで説明されているものよりも一般的な解決策を扱います(処理された型に関してはより一般的です)。です)。

2
Blaisorblade

x86命令セットは、結果を2つのレジスタに記憶する符号なし乗算命令を含む。 Cからその命令を使用するには、64ビットプログラム(gcc)で次のコードを書くことができます。

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

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

代替方法は、フラグレジスタをチェックするためにコンパイラ依存の本能を使用することである。オーバーフローの本能に関するGCCのドキュメントは https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html にあります。

0
Pauli Nieminen

mozilla::CheckedInt<T> は整数型Tに対してオーバーフローチェックされた整数数学を提供します(利用可能な場合はclangとgccのコンパイラ組み込み関数を使用)。コードはMPL 2.0の下にあり、3つの( IntegerTypeTraits.hAttributes.h および Compiler.h )他のヘッダーのみの非標準ライブラリヘッダーとMozilla固有の アサーション機構 に依存します。コードをインポートする場合は、おそらくアサーション機構を置き換えたいと思うでしょう。

0
hsivonen

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

0
Brian R. Bondy

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ビットの符号なし整数がまだうまく機能するという点で、machine-architecture safeを使用します。基本的に、最上位ビットを除くすべてをマスクするマスクを作成します。次に、両方の整数をマスクし、どちらかにそのビットが設定されていない場合は、加算は安全です。

これは変更されないので、コンストラクタでマスクを事前に初期化しておくとさらに高速になります。

0
Steztric