簡體   English   中英

用 C 檢測 uint64_t 整數的乘法溢出

[英]detecting multiplication of uint64_t integers overflow with C

是否有任何有效且可移植的方法來檢查在 C 中使用 int64_t 或 uint64_t 操作數的乘法運算何時溢出?

例如,添加 uint64_t 我可以這樣做:

if (UINT64_MAX - a < b) overflow_detected();
else sum = a + b;

實際上,相同的原理可用於乘法:

uint64_t a;
uint64_t b;
...
if (b != 0 && a > UINT64_MAX / b) { // if you multiply by b, you get: a * b > UINT64_MAX
    < error >
}
uint64_t c = a * b;

對於類似的有符號整數,你可能需要為每個符號組合提供一個案例。

如果你想在Ambroz的答案中避免分裂:

首先你必須看到兩個數字中較小的一個,比如a ,小於2 32 ,否則結果無論如何都會溢出。 b被分解為兩個32位字,即b = c 2 32 + d

那么計算並不那么困難,我發現:

uint64_t mult_with_overflow_check(uint64_t a, uint64_t b) {
  if (a > b) return mult_with_overflow_check(b, a);
  if (a > UINT32_MAX) overflow();
  uint32_t c = b >> 32;
  uint32_t d = UINT32_MAX & b;
  uint64_t r = a * c;
  uint64_t s = a * d;
  if (r > UINT32_MAX) overflow();
  r <<= 32;
  return addition_with_overflow_check(s, r);
}

所以這是兩次乘法,兩次換檔,一些加法和條件檢查。 這可能比除法更有效,因為例如兩個乘法可以並行流水線化。 您必須進行基准測試才能看到哪種方法更適合您。

與一些(希望)有用的答案相關的問題: 在C / C ++中檢測整數溢出的最佳方法 另外它不包括uint64_t ;)

case 6:
    for (a = 0; a < N; a++) {
        uint64_t b = a + c;
        uint64_t a1, b1;
        if (a > b) { a1 = a; b1 = b; }
        else       { a1 = b; b1 = a; }
        uint64_t cc = b1 * a1;
        c += cc;
        if (b1 > 0xffffffff) o++;
        else {
            uint64_t a1l = (a1 & 0xffffffff) + (a1 >> 32);
            a1l = (a1 + (a1 >> 32)) & 0xffffffff;
            uint64_t ab1l = a1l * b1;
            ab1l = (ab1l & 0xffffffff) + (ab1l >> 32);
            ab1l += (ab1l >> 32);
            uint64_t ccl = (cc & 0xffffffff) + (cc >> 32);
            ccl += (ccl >> 32);
            uint32_t ab32 = ab1l; if (ab32 == 0xffffffff) ab32 = 0;
            uint32_t cc32 = ccl; if (cc32 == 0xffffffff) cc32 = 0;
            if (ab32 != cc32) o++;
        }
    }
    break;

該方法將正常乘法的結果(可能溢出)與乘法結果進行比較,該乘法結果不會溢出。 所有計算都是模數(2 ^ 32 - 1)。

它更復雜,並且(很可能)不比Jens Gustedt的方法快。

經過一些小的修改后,它可以乘以96位精度(但沒有溢出控制)。 更有趣的是,該方法的思想可用於檢查一系列算術運算(乘法,加法,減法)的溢出。

有些問題得到解答

首先,關於"your code is not portable" 是的,代碼不可移植,因為它使用的是原始問題中請求的uint64_t 嚴格地說,你不能用(u)int64_t獲得任何便攜式答案,因為標准不要求它。

關於"once some overflow happens, you can not assume the result value to be anything" Standard表示無符號迭代不能溢出。 第6.2.5章,第9項:

涉及無符號操作數的計算永遠不會溢出,因為無法通過生成的無符號整數類型表示的結果將以比結果類型可以表示的最大值大1的數量為模。

因此,無符號的64位乘法以2 ^ 64為模進行,沒有溢出。

現在關於"logic behind""logic behind" “散列函數”在這里不是正確的單詞。 我只使用模數(2^32 - 1) 乘法的結果可以表示為n*2^64 + m ,其中m是可見結果, n表示我們溢出多少。 由於2^64 = 1 (mod 2^32 - 1) ,我們可以計算[true value] - [visible value] = (n*2^64 + m) - m = n*2^64 = n (mod 2^32 - 1) 如果n計算值不為零,則存在溢出。 如果為零,則沒有溢出。 只有在n >= 2^32 - 1之后才可能發生任何碰撞。 這將永遠不會發生,因為我們檢查其中一個被乘數小於2^32

它可能無法檢測到確切的溢出,但通常您可以在對數刻度上測試乘法的結果:

if (log(UINT64_MAX-1) - log(a) - log(b) < 0) overflow_detected(); // subtracting 1 to allow some tolerance when the numbers are converted to double
else prod = a * b;

這取決於你是否真的需要將乘法運算到精確的UINT64_MAX,否則這是檢查大數乘法的一種非常便攜和方便的方法。

還可以考慮使用編譯器的內置函數:

bool __builtin_mul_overflow (type1 a, type2 b, type3 *res)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM