我在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
你能告诉我,这里使用了什么算法的长除法(作者在评论中称之为“有趣”)?或者可能是它重写了常用算法的表达式?
答案 0 :(得分:4)
我已经制作了算法扩展的新版本来回答评论。
新版
作为输入,我们有一个64位整数v
,表示为一个十进制数字串。我们需要将其打包到two's complement format。结果有两个部分h
和l
(64位整数的高和低32位部分)
怎么做?
v
= h
* 2 ^ 32 + l
。这意味着h
是whole
2 ^ 32包含v
:h
= floor(v
/ 2 ^ 32)的数量。 l
是剩余部分:l
= v
%2^32
。我们需要计算它们。
我们需要一种数据类型来进行计算。在PHP上,我们有float
数据类型。它有a mantissa of 52 bits。尾数可以表示0到4 * 10 ^ 15加上某个(并且在负方向上几乎相同的范围)的整数。 float
可以代表32位PHP平台上最大的数字范围。所以这是进行计算的最佳选择。
我们需要选择一个divider
来分割v
,因为我们无法将其64位调整为float
的52位尾数。我们将其分为两部分hi
和lo
。 lo
包含由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)+ lo
。 l
= m
- q
* 2 ^ 32。现在我们有h
和l
两个部分。
为什么我们选择10^13
?我们要:
1.在计算过程中将所有数字拟合为52位
2.从10^13 / 2^32
(= 2328)获取一个整数(不是有理数),不会出错。 10 ^ 13最适合。
旧版
此代码使用浮点算术将给定数字v
打包为两个32位h
和l
部分。
代码的作者选择10^13
作为分隔符,使v
的部分符合double-precision floating-point的52位尾数而不会丢失有效位(2^51
大于{ {1}})。
算法说明:
给定的号码10^13
按v
分为两部分:
10^13
然后计算得到的数字的高位部分:
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
个数量。
2^32
实际上是以模数计算的:
l
。这个算法应该被重写吗?我认为应该以更清晰的方式重写。我还会在浮点数乘法后检查有效位的丢失。