javascript 32位整数乘法模拟

时间:2015-05-22 07:00:47

标签: javascript

我只想要像溢出的32位整数那样乘以int。

通过按位操作很容易理解的一些文章

function add32bit( a, b )
{
    return (a+b)|0;
}

和那样。

function mul32bit( a, b )
{
    return (a*b)|0;
}

但这不起作用。

在允许整数溢出的32位整数系统中。

calulate

12312311 * 1231231211 = -236858179

但是使用javascript

(12312311 * 1231231211)|0 = -236858180

有一种方法可以直接进行骚扰。

1 个答案:

答案 0 :(得分:1)

解决方案(?)

根据 Richie Frame 的提示,我尝试使用Karatsuba algorithm编程一个函数,该函数将两个整数相乘,并将结果作为带符号的32位整数返回。

// base algorithm from: https://en.wikipedia.org/wiki/Karatsuba_algorithm
// modified to discard values unneccessary for 32-Bit-operations

function int32_mul(x, y)
{
    // We use B = 2 and m = 16, because it will make sure that we only do multiplications with
    // 16 Bit per factor so that the result must have less than 32 Bit in total (which fits well
    // into a double).
    var bm = 1 << 16;

    x0 = x % bm;
    x1 = (x - x0) / bm;

    y0 = y % bm;
    y1 = (y - y0) / bm;

    // z1 + z0. We can discard z2 completely as it only contains a value out of our relevant bounds.
    // Both z1 and z0 are 32 Bit, but we shift out the top 16 Bit of z1.
    return (((x1 * y0 + x0 * y1) << 16) + (x0 * y0)) | 0;
}

我还进行了一些测试,以确保它有效,但我不是这个领域的专业人士,因此我无法保证它能够适用于所有组合。 TBH我的大脑对这个问题有点糊涂了。

var tests = [
    [ 0, 0, 0 ],
    [ 0, 1, 0 ],
    [ 1, 1 << 8, 256 ],
    [ 1, 1 << 16, 65536 ],
    [ 1, 1 << 24, 16777216 ],
    [ 1, 0x7fffffff, 2147483647 ],
    [ 1, 0x80000000, -2147483648 ],
    [ 1, 0xffffffff, -1 ],
    [ 2, 1 << 8, 512 ],
    [ 2, 1 << 16, 131072 ],
    [ 2, 1 << 24, 33554432 ],
    [ 2, 0x80000000, 0 ],
    [ 2, 0x7fffffff, -2 ],
    [ 2, 0xffffffff, -2 ],
    [ 256, 256, 65536 ],
    [ 65536, 65536, 0 ],
    [ -2, 2, -4 ],
    [ -65536, 65536, 0 ],
    [ -2, -2, 4 ],
    [ -2147483648, 1, -2147483648 ],
    [ -2147483649, 1, 2147483647 ],
    [ 12312311, 1231231211, -236858179 ],
];

for (var i = 0; i < tests.length; ++i)
{
    var test = tests[i];
    if (int32_mul(test[0], test[1]) !== test[2])
    { console.log(test[0], '*', test[1], '!==', test[2]); }
}

如果有更专业的人发现这一点,请发表评论和/或通过进一步的测试案例来启发我们。

此外,我不能说这个函数接受什么样的值。我非常确定它可以与所有带符号的32位整数一起使用,但它也可以使用无符号32位整数,甚至可以使用不超过68位的任何整数组合(共16个比特我们分开+ 52 Bits)(负数和正数,因为这个符号在双打中有一个位)。

问题解释

解释为什么JavaScript会给我们带来错误的结果。我使用C运行了一些简单的测试(在http://ideone.com/ELSgD0上乱搞它):

#include <stdio.h>
#include <stdint.h>

int main(void) {
    printf("%d\n", (int32_t)(12312311L * 1231231211L));
    printf("%llu\n", (uint64_t)12312311L * 1231231211L);
    printf("%.32f\n", 12312311. * 1231231211.);
    printf("%.8f\n", 15159301582738621.);
    return 0;
}

输出:

-236858179
15159301582738621
15159301582738620.00000000000000000000000000000000
15159301582738620.00000000

Javascript使用双精度进行所有计算(甚至是整数计算)。从上面的测试中我得出结论,double不能具有值15159301582738621

这是因为float(32位),double(64位)和quad(128位)等浮点数据类型的工作原理。基本上,它们不存储精确值,而是存储x形式的值的yx * 2^y值,这使它们能够存储非常大的值和非常小的值。我们(人类)使用类似的语法来表示非常大和小的数字,例如1e9为10亿,1e-50.00001,而e* 10^的快捷方式。

现在说你计算10001 * 10001,显然是100020001,但想象你只能存储5个数字和1个指数。要存储结果,您必须对其进行近似并使用例如1.0002e810002e4。正如你所看到的,你必须忘记最后的1。实际上,你的exaple中双打的问题非常相似,只是规模较大而基数为2而不是10。

最后两个printf语句证明双打不能保存值15159301582738621,这是您的示例计算的确切结果。如果您使用12312311 * 1231231212(第二个数字末尾的2而不是1)尝试它,您将看到并非此范围内的所有数字都不能存储为双精度,因为该计算适用于您的函数