我正在尝试优化31.32 fixed-point math library written in C#的乘法代码。
(错误的)伪代码是:
long result = (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#代码来使用它。
有什么想法吗?
答案 0 :(得分:-4)
浮点数总是不精确。 It is in their nature。试图使用不带浮点精度的浮点数或没有溢出的任何整数类型就像试图在没有空气的情况下进行飞行。
解决方案是使用非常大的整数(仅在输出期间显示十进制标记)或固定宽度的十进制类型(在幕后是整数)。如果你真的是一个非常大的整数,那就是BigInteger Structure。它的上限是&#34;无论GC可以分配什么&#34;它可能会遇到OOM异常。
至于速度,有两件事需要考虑: