了解长除法算法

时间:2015-02-21 01:49:08

标签: algorithm long-integer

我在PHP的SphinxAPI类中创建了一些长除法代码,它将64位int分成两个32位整数,并在bc库不可用时在32位机器上使用:

    // x32, no-bcmath
    $p = max(0, strlen($v) - 13);
    $lo = abs((float)substr($v, $p));
    $hi = abs((float)substr($v, 0, $p));

    $m = $lo + $hi*1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912
    $q = floor($m/4294967296.0);
    $l = $m - ($q*4294967296.0);
    $h = $hi*2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328

你能告诉我,这里使用了什么算法的长除法(作者在评论中称之为“有趣”)?或者可能是它重写了常用算法的表达式?

1 个答案:

答案 0 :(得分:4)

我已经制作了算法扩展的新版本来回答评论。

新版

作为输入,我们有一个64位整数v,表示为一个十进制数字串。我们需要将其打包到two's complement format。结果有两个部分hl(64位整数的高和低32位部分)

怎么做?

v = h * 2 ^ 32 + l。这意味着hwhole 2 ^ 32包含vh = floor(v / 2 ^ 32)的数量。 l是剩余部分:l = v2^32。我们需要计算它们。

我们需要一种数据类型来进行计算。在PHP上,我们有float数据类型。它有a mantissa of 52 bits。尾数可以表示0到4 * 10 ^ 15加上某个(并且在负方向上几乎相同的范围)的整数。 float可以代表32位PHP平台上最大的数字范围。所以这是进行计算的最佳选择。

我们需要选择一个divider来分割v,因为我们无法将其64位调整为float的52位尾数。我们将其分为两部分hilolo包含由v的13个低十进制数字表示的数字,而hi表示其他部分的数字:v = hi * 10 ^ 13 + lo。 (稍后我们将解释为什么选择10^13

hi包含h1 = hi * floor(10 ^ 13/2 ^ 32)次2 ^ 32。但是提醒(余数意味着hi *(10 ^ 13%2 ^ 32))和lo也可以包含一些2 ^ 32。我们算一算:h2 = q = floor(hi *(10 ^ 13%2 ^ 32)+ lo)/ 2 ^ 32。并且h = h1 + h2

让我们介绍m = hi *(10 ^ 13%2 ^ 32)+ lol = m - q * 2 ^ 32。现在我们有hl两个部分。

为什么我们选择10^13?我们要: 1.在计算过程中将所有数字拟合为52位 2.从10^13 / 2^32(= 2328)获取一个整数(不是有理数),不会出错。 10 ^ 13最适合。


旧版

此代码使用浮点算术将给定数字v打包为两个32位hl部分。

代码的作者选择10^13作为分隔符,使v的部分符合double-precision floating-point的52位尾数而不会丢失有效位(2^51大于{ {1}})。

算法说明:

  1. 给定的号码10^13v分为两部分:

    • 10^13
  2. 然后计算得到的数字的高位部分:

    • v = hi * 10^13 + lo

    • 其中h = (10^13 / 2^32) * hi + (m / 2^32)

    • 在此,我们计算给定数字m = lo + hi * (10^32 % 2^32)中包含的2^32个数,以填充生成的64位整数的高v部分。棘手的部分是h。我们需要它将剩余的“金额”从m添加到hi,并计算其中包含的lo个数量。

  3. 2^32实际上是以模数计算的:

    • l
  4. 这个算法应该被重写吗?我认为应该以更清晰的方式重写。我还会在浮点数乘法后检查有效位的丢失。