使用双字结果进行高效的长乘法

时间:2018-02-27 11:54:17

标签: c# .net

我正在尝试优化31.32 fixed-point math library written in C#的乘法代码。

(错误的)伪代码是: long result = (a * b) >> 32;

问题当然是a * b在向下移动之前可能会溢出。即使(a * b) >> 32(乘法的最终结果)在long的值范围内,中间值a * b也可能不是。

通常的解决方案是将ab分别分为低和高部分,并在乘法步骤之前对高部分执行移位操作。这避免了中间值的溢出,但使代码更加复杂:

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#代码来使用它。

有什么想法吗?

1 个答案:

答案 0 :(得分:-4)

浮点数总是不精确。 It is in their nature。试图使用不带浮点精度的浮点数或没有溢出的任何整数类型就像试图在没有空气的情况下进行飞行。

解决方案是使用非常大的整数(仅在输出期间显示十进制标记)或固定宽度的十进制类型(在幕后是整数)。如果你真的是一个非常大的整数,那就是BigInteger Structure。它的上限是&#34;无论GC可以分配什么&#34;它可能会遇到OOM异常。

至于速度,有两件事需要考虑:

  1. The Speed Rant
  2. 根据.NET Framework的性质,您只能控制速度。每当GC决定收集时,您将停止所有线程并获得间歇性停顿。虽然您可以使其有效,但您永远不能在.NET中创建实时应用程序(具有保证的响应时间)。如果这是你的目标,那么.NET就是错误的方向。