JavaScript中的32位有符号乘法与64位结果

JavaScript中的32位有符号乘法与64位结果,javascript,algorithm,integer,multiplication,Javascript,Algorithm,Integer,Multiplication,我正在用JavaScript开发一个VM,需要将两个有符号的32位数字与一个64位有符号结果相乘,该结果存储为两个32位有符号数字上32位和下32位 我设法对无符号数做了同样的处理,将两个数拆分为16位对,然后将它们相乘:a*b=ah*2^16+al*bh*2^16+bl: 然而,我真的不明白如何对有符号的数字做同样的事情。我能想到的唯一一件事是对任何负的a或b求反,使两者都为正,执行无符号乘法,然后根据需要对结果求反,但这感觉像是一个无厘头的次优解决方案。关于如何做得更好有什么想法吗?将a和b

我正在用JavaScript开发一个VM,需要将两个有符号的32位数字与一个64位有符号结果相乘,该结果存储为两个32位有符号数字上32位和下32位

我设法对无符号数做了同样的处理,将两个数拆分为16位对,然后将它们相乘:a*b=ah*2^16+al*bh*2^16+bl:

然而,我真的不明白如何对有符号的数字做同样的事情。我能想到的唯一一件事是对任何负的a或b求反,使两者都为正,执行无符号乘法,然后根据需要对结果求反,但这感觉像是一个无厘头的次优解决方案。关于如何做得更好有什么想法吗?将a和b拆分为两个有符号的16位数字似乎是合乎逻辑的,但随后我对如何在没有任何错误的情况下执行其余部分感到茫然


p、 如果您认为我的无符号实现也不太理想,也请随意指出。

将有符号32位整数拆分为两个16位整数的正确方法是有符号16位上半部分和无符号16位下半部分,您需要对负数进行调整,上半部分减去一,下半部分加2^16,使之为正

例如,数字-100000应该变成-2的上半部分和31072的下半部分。你可以通过重构-2*2^16+31072==-131072+31072==-100000来看到

在这之后,你可以像平常一样做交叉乘法算法;结果的上半部分是有符号的32位整数,因为它是一些有符号的乘积的和,下半部分是无符号的32位整数。解释它涉及到反过来做同样的把戏


顺便说一句,这相当于一种相当自然的解释,如果你观察机器上以本机方式进行乘法的本机整数的单个字,你会看到这种解释。

将有符号32位整数拆分为两个16位整数的正确方法是将有符号16位上半部分,还有一个无符号的16位下半部分-你需要对负数进行调整,从上半部分减去一,然后在下半部分加上2^16,使其为正数

例如,数字-100000应该变成-2的上半部分和31072的下半部分。你可以通过重构-2*2^16+31072==-131072+31072==-100000来看到

在这之后,你可以像平常一样做交叉乘法算法;结果的上半部分是有符号的32位整数,因为它是一些有符号的乘积的和,下半部分是无符号的32位整数。解释它涉及到反过来做同样的把戏


顺便说一句,这相当于一个相当自然的解释,如果你观察机器上以本机方式进行乘法运算的本机整数的各个单词,你会看到这个解释。

我发现自己也有同样的问题,但没有找到完整的答案。这不是小事。所以我在这里提出了一个解决方案:

if (!Math.umul32_64) {
    Math.umul32_64 = function (a, b, result) {
        if (result === undefined) result = [0, 0];

        a >>>= 0;
        b >>>= 0;

        if (a < 32767 && b < 65536) {
            result[0] = a * b;
            result[1] = (result[0] < 0) ? -1 : 0;
            return result;
        }

        var a00 = a & 0xFFFF, a16 = a >>> 16;
        var b00 = b & 0xFFFF, b16 = b >>> 16;

        var c00 = a00 * b00;
        var c16 = (c00 >>> 16) + (a16 * b00);
        var c32 = c16 >>> 16;
        c16 = (c16 & 0xFFFF) + (a00 * b16);
        c32 += c16 >>> 16;
        var c48 = c32 >>> 16;
        c32 = (c32 & 0xFFFF) + (a16 * b16);
        c48 += c32 >>> 16;

        result[0] = ((c16 & 0xFFFF) << 16) | (c00 & 0xFFFF);
        result[1] = ((c48 & 0xFFFF) << 16) | (c32 & 0xFFFF);
        return result;
    };
}

if (!Math.imul32_64) {
    Math.imul32_64 = function (a, b, result) {
        if (result === undefined) result = [0, 0];

        if (a == 0) return result[0] = result[1] = 0, result;
        if (b == 0) return result[0] = result[1] = 0, result;

        a |= 0, b |= 0;

        if ((a >= -32768 && a <= 32767) && (b >= -32768 && b <= 32767)) {
            result[0] = a * b;
            result[1] = (result[0] < 0) ? -1 : 0;
            return result;
        }

        var doNegate = (a < 0) ^ (b < 0);

        Math.umul32_64(Math.abs(a), Math.abs(b), result);

        if (doNegate) {
            result[0] = ~result[0];
            result[1] = ~result[1];
            result[0] = (result[0] + 1) | 0;
            if (result[0] == 0) result[1] = (result[1] + 1) | 0;
        }

        return result;
    };
}

我发现自己也有同样的问题,没有找到完全的答案。这不是小事。所以我在这里提出了一个解决方案:

if (!Math.umul32_64) {
    Math.umul32_64 = function (a, b, result) {
        if (result === undefined) result = [0, 0];

        a >>>= 0;
        b >>>= 0;

        if (a < 32767 && b < 65536) {
            result[0] = a * b;
            result[1] = (result[0] < 0) ? -1 : 0;
            return result;
        }

        var a00 = a & 0xFFFF, a16 = a >>> 16;
        var b00 = b & 0xFFFF, b16 = b >>> 16;

        var c00 = a00 * b00;
        var c16 = (c00 >>> 16) + (a16 * b00);
        var c32 = c16 >>> 16;
        c16 = (c16 & 0xFFFF) + (a00 * b16);
        c32 += c16 >>> 16;
        var c48 = c32 >>> 16;
        c32 = (c32 & 0xFFFF) + (a16 * b16);
        c48 += c32 >>> 16;

        result[0] = ((c16 & 0xFFFF) << 16) | (c00 & 0xFFFF);
        result[1] = ((c48 & 0xFFFF) << 16) | (c32 & 0xFFFF);
        return result;
    };
}

if (!Math.imul32_64) {
    Math.imul32_64 = function (a, b, result) {
        if (result === undefined) result = [0, 0];

        if (a == 0) return result[0] = result[1] = 0, result;
        if (b == 0) return result[0] = result[1] = 0, result;

        a |= 0, b |= 0;

        if ((a >= -32768 && a <= 32767) && (b >= -32768 && b <= 32767)) {
            result[0] = a * b;
            result[1] = (result[0] < 0) ? -1 : 0;
            return result;
        }

        var doNegate = (a < 0) ^ (b < 0);

        Math.umul32_64(Math.abs(a), Math.abs(b), result);

        if (doNegate) {
            result[0] = ~result[0];
            result[1] = ~result[1];
            result[0] = (result[0] + 1) | 0;
            if (result[0] == 0) result[1] = (result[1] + 1) | 0;
        }

        return result;
    };
}