我正在尝试优化 31.32 fixed-point math library written in C# 的乘法代码.
(不正确的)伪代码是:
长结果 = (a * b) >> 32;
问题当然是 a * b
在被下移之前的潜在溢出。即使(a * b) >> 32
(乘法的最终结果)在long
的取值范围内,中间值a * b
可能不是。
通常的解决方案是将a
和b
分别拆分为低位和高位,并在乘法步骤之前对高位进行移位操作。这避免了中间值的溢出,但使代码更加复杂:
var xl = x.m_rawValue;
var yl = y.m_rawValue;
var xlo = (ulong)(xl & 0x00000000FFFFFFFF);
var xhi = xl >> FRACTIONAL_PLACES;
var ylo = (ulong)(yl & 0x00000000FFFFFFFF);
var yhi = yl >> FRACTIONAL_PLACES;
var lolo = xlo * ylo;
var lohi = (long)xlo * yhi;
var hilo = xhi * (long)ylo;
var hihi = xhi * yhi;
var loResult = lolo >> FRACTIONAL_PLACES;
var midResult1 = lohi;
var midResult2 = hilo;
var hiResult = hihi << FRACTIONAL_PLACES;
var sum = (long)loResult + midResult1 + midResult2 + hiResult;
生成的机器代码同样复杂。
x86 imul
指令可以在一条指令中的两个寄存器中返回一个双字结果,但我不知道如何编写编译器可以优化使用它的 C# 代码。
有什么想法吗?
最佳答案
Core 3.0 添加了对您正在寻找的 imul x64 CPU 指令的支持:
ulong lo64Bits;
ulong hi64Bits= System.Runtime.Intrinsics.X86.Bmi2.X64.MultiplyNoFlags(a, b, &lo);
(当然仅限于x86 arch)
关于c# - 双字结果的高效长乘法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49008086/