問題描述
我可以對 64 位整數(shù)進行乘除運算的最準確方法是在 32 位和 64 位程序中(在 Visual C++ 中)嗎?(在溢出的情況下,我需要結果 mod 264.)
What is the most accurate way I can do a multiply-and-divide operation for 64-bit integers that works in both 32-bit and 64-bit programs (in Visual C++)? (In case of overflow, I need the result mod 264.)
(我正在尋找類似 MulDiv64 的東西,除了這個使用內聯(lián)匯編,僅適用于 32 位程序.)
(I'm looking for something like MulDiv64, except that this one uses inline assembly, which only works in 32-bit programs.)
顯然,轉換為 double
并返回是可能的,但我想知道是否有更準確的方法而不是太復雜.(即,我不是在這里尋找任意精度的算術庫!)
Obviously, casting to double
and back is possible, but I'm wondering if there's a more accurate way that isn't too complicated. (i.e. I'm not looking for arbitrary-precision arithmetic libraries here!)
推薦答案
由于這被標記為 Visual C++,我將提供一個濫用 MSVC 特定內在函數(shù)的解決方案.
這個例子相當復雜.它是 GMP 和 java.math.BigInteger
用于大除法的相同算法的高度簡化版本.
This example is fairly complicated. It's a highly simplified version of the same algorithm that is used by GMP and java.math.BigInteger
for large division.
雖然我有一個更簡單的算法,但它可能慢了大約 30 倍.
Although I have a simpler algorithm in mind, it's probably about 30x slower.
此解決方案具有以下約束/行為:
- 它需要 x64.它不會在 x86 上編譯.
- 商不為零.
- 商在 64 位溢出時飽和.
請注意,這是針對無符號整數(shù)的情況.圍繞它構建一個包裝器以使其也適用于已簽名的案例是微不足道的.此示例還應生成正確截斷的結果.
Note that this is for the unsigned integer case. It's trivial to build a wrapper around this to make it work for signed cases as well. This example should also produce correctly truncated results.
這段代碼沒有經過全面測試.但是,它已經通過了我拋出的所有測試用例.
(即使是我故意構建以試圖破壞的用例算法.)
This code is not fully tested. However, it has passed all the tests cases that I've thrown at it.
(Even cases that I've intentionally constructed to try to break the algorithm.)
#include <intrin.h>
uint64_t muldiv2(uint64_t a, uint64_t b, uint64_t c){
// Normalize divisor
unsigned long shift;
_BitScanReverse64(&shift,c);
shift = 63 - shift;
c <<= shift;
// Multiply
a = _umul128(a,b,&b);
if (((b << shift) >> shift) != b){
cout << "Overflow" << endl;
return 0xffffffffffffffff;
}
b = __shiftleft128(a,b,shift);
a <<= shift;
uint32_t div;
uint32_t q0,q1;
uint64_t t0,t1;
// 1st Reduction
div = (uint32_t)(c >> 32);
t0 = b / div;
if (t0 > 0xffffffff)
t0 = 0xffffffff;
q1 = (uint32_t)t0;
while (1){
t0 = _umul128(c,(uint64_t)q1 << 32,&t1);
if (t1 < b || (t1 == b && t0 <= a))
break;
q1--;
// cout << "correction 0" << endl;
}
b -= t1;
if (t0 > a) b--;
a -= t0;
if (b > 0xffffffff){
cout << "Overflow" << endl;
return 0xffffffffffffffff;
}
// 2nd reduction
t0 = ((b << 32) | (a >> 32)) / div;
if (t0 > 0xffffffff)
t0 = 0xffffffff;
q0 = (uint32_t)t0;
while (1){
t0 = _umul128(c,q0,&t1);
if (t1 < b || (t1 == b && t0 <= a))
break;
q0--;
// cout << "correction 1" << endl;
}
// // (a - t0) gives the modulus.
// a -= t0;
return ((uint64_t)q1 << 32) | q0;
}
請注意,如果您不需要完美截斷的結果,則可以完全刪除最后一個循環(huán).如果這樣做,答案將不會比正確的商大 2.
Note that if you don't need a perfectly truncated result, you can remove the last loop completely. If you do this, the answer will be no more than 2 larger than the correct quotient.
測試用例:
cout << muldiv2(4984198405165151231,6132198419878046132,9156498145135109843) << endl;
cout << muldiv2(11540173641653250113, 10150593219136339683, 13592284235543989460) << endl;
cout << muldiv2(449033535071450778, 3155170653582908051, 4945421831474875872) << endl;
cout << muldiv2(303601908757, 829267376026, 659820219978) << endl;
cout << muldiv2(449033535071450778, 829267376026, 659820219978) << endl;
cout << muldiv2(1234568, 829267376026, 1) << endl;
cout << muldiv2(6991754535226557229, 7798003721120799096, 4923601287520449332) << endl;
cout << muldiv2(9223372036854775808, 2147483648, 18446744073709551615) << endl;
cout << muldiv2(9223372032559808512, 9223372036854775807, 9223372036854775807) << endl;
cout << muldiv2(9223372032559808512, 9223372036854775807, 12) << endl;
cout << muldiv2(18446744073709551615, 18446744073709551615, 9223372036854775808) << endl;
輸出:
3337967539561099935
8618095846487663363
286482625873293138
381569328444
564348969767547451
1023786965885666768
11073546515850664288
1073741824
9223372032559808512
Overflow
18446744073709551615
Overflow
18446744073709551615
這篇關于在 64 位中進行組合乘除運算的最準確方法是什么?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!