繁体   English   中英

为什么这个除法中有一个循环作为乘法代码?

[英]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
}}

在 C 代码中:

auto magic = (1ULL<<32)/test_divisor;

我们在magic中得到整数值,因为(1ULL<<32)test_divisor都是整数。
算法需要在某些条件下增加magic ,这是下一个条件语句。

现在,乘法也给出整数:
auto answer1 = (a*magic) >> 32;
auto answer2 = (b*magic) >> 32;

C 代码完成!

在 JS 代码中:

所有变量都是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 / da * m_ceila * m_js

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM