[英]Why is there a loop in this division as multiplication code?
我从一个黑客喜悦的档案中得到了下面的 js 代码(查看源代码)
该代码接受一个值(例如 7)并输出一个幻数以与之相乘。 然后你bitshift得到结果。 我不记得汇编或任何数学,所以我确定我错了,但我找不到我错的原因
据我了解,您可以通过编写 ceil(1/divide * 1<<32
) (或<<64
对于 64 位值,但您需要更大的整数)来获得一个幻数。 如果您将整数与 imul 相乘,您将在一个寄存器中获得结果,而在另一个寄存器中获得余数。 结果寄存器神奇地是用我的公式中的这个幻数除法的正确结果
我写了一些 C++ 代码来说明我的意思。 但是我只测试了下面的值。 这似乎是正确的。 JS 代码有一个循环等等,我想知道,为什么? 我错过了什么吗? 我可以使用哪些值来获得 JS 代码正确获得的错误结果? 我数学不是很好,所以我不明白任何评论
#include <cstdio>
#include <cassert>
int main(int argc, char *argv[])
{
auto test_divisor = 7;
auto test_value = 43;
auto a = test_value*test_divisor;
auto b = a-1; //One less test
auto magic = (1ULL<<32)/test_divisor;
if (((1ULL<<32)%test_divisor) != 0) {
magic++; //Round up
}
auto answer1 = (a*magic) >> 32;
auto answer2 = (b*magic) >> 32;
assert(answer1 == test_value);
assert(answer2 == test_value-1);
printf("%lld %lld\n", answer1, answer2);
}
来自黑客的 JS 代码令人欣喜
var two31 = 0x80000000
var two32 = 0x100000000
function magic_signed(d) { with(Math) {
if (d >= two31) d = d - two32// Treat large positive as short for negative.
var ad = abs(d)
var t = two31 + (d >>> 31)
var anc = t - 1 - t%ad // Absolute value of nc.
var p = 31 // Init p.
var q1 = floor(two31/anc) // Init q1 = 2**p/|nc|.
var r1 = two31 - q1*anc // Init r1 = rem(2**p, |nc|).
var q2 = floor(two31/ad) // Init q2 = 2**p/|d|.
var r2 = two31 - q2*ad // Init r2 = rem(2**p, |d|).
do {
p = p + 1;
q1 = 2*q1; // Update q1 = 2**p/|nc|.
r1 = 2*r1; // Update r1 = rem(2**p, |nc|.
if (r1 >= anc) { // (Must be an unsigned
q1 = q1 + 1; // comparison here).
r1 = r1 - anc;}
q2 = 2*q2; // Update q2 = 2**p/|d|.
r2 = 2*r2; // Update r2 = rem(2**p, |d|.
if (r2 >= ad) { // (Must be an unsigned
q2 = q2 + 1; // comparison here).
r2 = r2 - ad;}
var delta = ad - r2;
} while (q1 < delta || (q1 == delta && r1 == 0))
var mag = q2 + 1
if (d < 0) mag = two32 - mag // Magic number and
shift = p - 32 // shift amount to return.
return mag
}}
auto magic = (1ULL<<32)/test_divisor;
我们在magic
中得到整数值,因为(1ULL<<32)
和test_divisor
都是整数。
算法需要在某些条件下增加magic
,这是下一个条件语句。
现在,乘法也给出整数:
auto answer1 = (a*magic) >> 32;
auto answer2 = (b*magic) >> 32;
C 代码完成!
所有变量都是var
; 没有数据类型!
没有整数除法; 没有整数乘法!
按位运算并不容易,也不适合在该算法中使用。 数字数据是通过 Number 和 BigInt 实现的,它们不像“C Int”或“C Unsigned Long Long”。
因此,该算法使用循环迭代地添加和比较“除法和乘法”是否发生在最近的整数内。
首先,我认为ceil(1/divide * 1<<32)
可以,根据除法,有结果为 1 的情况。 所以你不需要一个循环,但有时你需要一个校正因子。
其次,JS 代码似乎允许除32
之外的其他移位: shift = p - 32 // shift amount to return
。 但是它永远不会返回那个。 所以不确定那里发生了什么。
为什么不在 C++ 中实现 JS 代码,然后在所有 int32_t 上运行一个循环,看看它们是否给出相同的结果? 这不应该花太长时间。
当您找到它们不同的d
时,您可以使用两个幻数测试所有int32_t a
a a / d
d 并比较a / d
、 a * m_ceil
和a * m_js
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.