如何快速计算 100 位数字的乘积
How do I quickly compute the product of 100bit numbers
我正在尝试计算两个 100 位数的乘积。它应该模仿 100 位 CPU 架构原生的无符号整数乘法行为。也就是说,程序必须计算实际产品,对 2^100 取模。
为了快速做到这一点,我选择将 100 位数字实现为 uint64_t[2],一个 64 位数字的二元数组。更准确地说,x = 2^64 * a + b。我需要快速执行算术和逻辑运算(乘积、位移、位旋转、异或等)。我选择这种表示是因为它允许我对 64 位成分使用快速、本机的操作。例如,旋转 128 位 'number' 仅比旋转 64 位 int 慢两倍。 Boost::128bit 慢得多并且 bitset 和 valarray 没有算术。我可以将数组用于除乘法以外的所有操作,然后将数组转换为 boost:128bit 然后乘法,但这是最后的手段,可能非常慢。
我已经尝试关注了。让我们有两对这样的 64 位数字,比如 2^64 a + b 和 2^64 x + y。那么乘积可以表示为
2^128 ax + 2^64 (ay + bx) + by
我们可以忽略第一项,因为它太大了。拿一对
就差不多了
ay + bx, by
是我们的答案,但更重要的一半是 'missing' b*y 操作的溢出。如果不将数字 b、y 分成四个不同的 32 位,并使用分而治之的方法来确保乘积的扩展项不会溢出,我不知道如何计算这个。
这是针对 'chess engine' 在 10x10 板上使用魔法乘法散列
对于可能产生的溢出,您只关心 b * y
中每个数字的最高 32 位:
struct Num {
uint64_t low;
uint64_t high;
Num &operator*=(const Num &o) {
high = low * o.high +
high * o.low +
(low >> 32u) * (o.low >> 32u); // <- handles overflow
low *= o.low;
high &= 0xFFFFFFFFF; // keeping number 100 bits
return *this;
}
};
查看您的 cpu 是否支持任何本机 128 位整数,因为那将是最佳的(尽管不可移植)。
祝您的国际象棋引擎好运!
想到就借用:
他一心想 100 位,使用 64 位高位和仅 36 位低位误差会更小:
您可以使用 (low >> 4u) * (o.low >> 4u)
计算 "low×low" 的最高 64 位,并将其高 36 位用作溢出到高位。
毫不费力地为魔术文字创造名称:
Bits100 &operator*=(const Bits100 &o) {
high = low * o.high + // ignore high * o.high
high * o.low +
(low >> 4u) * (o.low >> 4u) >> 28; // handles overflow in most cases
low = low * o.low & 0xFFFFFFFFF; // keep low to 100-64 bits
return *this;
}
我正在尝试计算两个 100 位数的乘积。它应该模仿 100 位 CPU 架构原生的无符号整数乘法行为。也就是说,程序必须计算实际产品,对 2^100 取模。
为了快速做到这一点,我选择将 100 位数字实现为 uint64_t[2],一个 64 位数字的二元数组。更准确地说,x = 2^64 * a + b。我需要快速执行算术和逻辑运算(乘积、位移、位旋转、异或等)。我选择这种表示是因为它允许我对 64 位成分使用快速、本机的操作。例如,旋转 128 位 'number' 仅比旋转 64 位 int 慢两倍。 Boost::128bit 慢得多并且 bitset 和 valarray 没有算术。我可以将数组用于除乘法以外的所有操作,然后将数组转换为 boost:128bit 然后乘法,但这是最后的手段,可能非常慢。
我已经尝试关注了。让我们有两对这样的 64 位数字,比如 2^64 a + b 和 2^64 x + y。那么乘积可以表示为
2^128 ax + 2^64 (ay + bx) + by
我们可以忽略第一项,因为它太大了。拿一对
就差不多了ay + bx, by
是我们的答案,但更重要的一半是 'missing' b*y 操作的溢出。如果不将数字 b、y 分成四个不同的 32 位,并使用分而治之的方法来确保乘积的扩展项不会溢出,我不知道如何计算这个。
这是针对 'chess engine' 在 10x10 板上使用魔法乘法散列
对于可能产生的溢出,您只关心 b * y
中每个数字的最高 32 位:
struct Num {
uint64_t low;
uint64_t high;
Num &operator*=(const Num &o) {
high = low * o.high +
high * o.low +
(low >> 32u) * (o.low >> 32u); // <- handles overflow
low *= o.low;
high &= 0xFFFFFFFFF; // keeping number 100 bits
return *this;
}
};
查看您的 cpu 是否支持任何本机 128 位整数,因为那将是最佳的(尽管不可移植)。
祝您的国际象棋引擎好运!
想到就借用
他一心想 100 位,使用 64 位高位和仅 36 位低位误差会更小:
您可以使用 (low >> 4u) * (o.low >> 4u)
计算 "low×low" 的最高 64 位,并将其高 36 位用作溢出到高位。
毫不费力地为魔术文字创造名称:
Bits100 &operator*=(const Bits100 &o) {
high = low * o.high + // ignore high * o.high
high * o.low +
(low >> 4u) * (o.low >> 4u) >> 28; // handles overflow in most cases
low = low * o.low & 0xFFFFFFFFF; // keep low to 100-64 bits
return *this;
}