计算(64位无符号整数)*(64位无符号整数)除以2 ^ 64的商

时间:2018-05-26 10:22:48

标签: c++ bit-manipulation 64-bit multiplication 128-bit

我想计算“两个64位无符号整数的乘法”除以2 ^ 64的商。

有人说128位整数能够使用,但在某些编程语言或某些平台(如visual studio c ++)中不支持内置的128位整数。

但是我们不想使用除法,因为除法需要时间太多。我认为可以通过加/减,乘法和按位运算(如位移)来完成。

2 个答案:

答案 0 :(得分:1)

将你的数字分成两部分(使用位移和位掩码)并应用一些代数。

  • 第一个号码:A*2^32 + C,其中AC均小于2 ^ 32。
  • 第二个号码:B*2^32 + D,其中BD均小于2 ^ 32。
  • (A*2^32 + C) * (B*2^32 + D) = (A*B)*2^64 + (A*D)*2^32 + (B*C)*2^32 + (C*D)
  • 除以2 ^ 64:(A*B) + (A*D)/2^32 + (B*C)/2^32 + (C*D)/2^64

所以答案几乎是(A*B) + (A*D)>>32 + (B*C)>>32,但这可能会导致舍入错误。错误是什么?从真实(浮点)商中减去这个几乎答案:

  • (A*D)&0xFFFFFFFF/2^32 + (B*C)&0xFFFFFFFF/2^32 + (C*D)/2^64(请将分部视为"真实"或浮点数。)
  • = [(A*D)&0xFFFFFFFF + (B*C)&0xFFFFFFFF + (C*D)/2^32] / 2^32(再次,真正的分裂)
  • = [(A*D)&0xFFFFFFFF + (B*C)&0xFFFFFFFF + (C*D)>>32] >> 32加上少于1的东西。

所以你可以得到所需的号码 (A*B) + (A*D)>>32 + (B*C)>>32 + [(A*D)&0xFFFFFFFF + (B*C)&0xFFFFFFFF + (C*D)>>32] >> 32

答案 1 :(得分:0)

将数字a和b分解为32位部分:

a = a1 * 2**32 + a0
b = b1 * 2**32 + b0
a * b = (a1 * b1) * 2**64 + (a1 * b0 + a0 * b1) * 2**32 + a0 * b0

要获得结果的64位高位,a1 * b1部分是显而易见的,另一部分应该被分解:首先将a1 * b0的32位高位加到a0 * b1的32位高位;第二个将(a1 * b0 + a0 * b1)的32个低位添加到a0 * b0的32个高位,并保留该中间结果的32个高位(实际为1个有效位),以便考虑从低位溢出丢弃之前的位。

以下是对结果进行基本检查的代码。

#include <cstdint>
#include <iostream>

using namespace std;

inline uint64_t high32(uint64_t x) {
    return x >> 32;
}

inline uint64_t low32(uint64_t x) {
    return static_cast<uint32_t>(x);
}

uint64_t mul64(uint64_t a, uint64_t b)
{
    uint64_t a1 = high32(a);
    uint64_t a0 = low32(a);
    uint64_t b1 = high32(b);
    uint64_t b0 = low32(b);

    uint64_t a1_b0 = a1 * b0;
    uint64_t a0_b1 = a0 * b1;

    return a1 * b1 + high32(a1_b0) + high32(a0_b1)
         + high32(low32(a1_b0 + a0_b1) + high32(a0 * b0));
}

int main()
{
    cout << mul64(UINT64_MAX, UINT64_MAX) << endl;
    cout << UINT64_MAX - 1 << endl;

    cout << endl;
    cout << mul64(UINT64_MAX - 1, UINT64_MAX) << endl;
    cout << UINT64_MAX - 2 << endl;

    cout << endl;
    cout << mul64(UINT64_MAX - 2, UINT64_MAX) << endl;
    cout << UINT64_MAX - 3 << endl;
}