【问题标题】:Adding hexadecimal strings in JavaScript efficiently在 JavaScript 中有效地添加十六进制字符串
【发布时间】:2014-06-11 02:18:23
【问题描述】:

在 JavaScript 中,我有两个变量,每个变量都包含一个十六进制数字作为字符串。例如:

var a = 'a3bc',
    b = '1d0f';

现在我想添加它们(所以,结果应该是'c0cb')。为了让事情变得更容易一些,让我们对此进行一些限制:

  • 数字始终由相同的数字组成(即字符串的长度相同)。
  • 如有必要,号码会以0s 为前缀,因此将是'001a',而不仅仅是'1a'

另一方面,有一些限制让事情变得有点困难:

  • 数字不是上例中的四位数字,而是 20 位数字。因此,您不能简单地将它们转换为十进制,将它们相加,然后再将它们转换回来。换句话说:对于 JavaScript 的 number 类型来说,数字太大了(这就是为什么 this answer 不起作用)。
  • 不允许溢出。如果添加'ffff''0001',则结果应为'0000',而不是'10000'。换句话说:所有计算都必须使用模除法。

我目前有一个算法可以解决所有这些问题,但它很长,效率不高,而且很优雅。它的想法是逐个字符地遍历字符串,将它们转换为十进制,添加它们,将它们转换回来,记住潜在的溢出等等。如前所述,它工作得很好,但我认为这不是最好的解决方案。

我怎样才能更好地解决这个问题?

PS:我需要在 Node.js 中执行此操作,因此如果有可用的现成模块可以执行此操作,我完全可以:-)

【问题讨论】:

  • 您知道 JavaScript 不会转换为十进制格式,而是转换为 64 位二进制浮点双精度数吗?由于您的输入具有 80 位,因此没有基于处理器的格式来存储它。但是您可以将 16 位或 32 位的 4 或 8 个字符块放入双浮点数中,这将加快基于字符串的计算。 -- 如果最后的进位不是 0,即 1,则在字符串中添加一个额外的 1。在这里,只要新进位为 0,就可以停止处理字符串。
  • 实际上,它是 160 位 ;-)。但是,尽管如此,我喜欢这个主意。只需要弄清楚如何以一种很好的方式做到这一点:-))。
  • 一个十六进制数字是 4 位,20 个十六进制数字是 80 位,初等数学。而这个加块就是添加到 MvG 的答案中的,所以我不必克隆它。
  • 不想争论小事,但是:ASCII 编码 string 中的一个“数字”等于 8 位,因此在我的示例中为 20 hex-数字是 160 位。初等数学。
  • 对不起,如果我给了错误或不完整的印象,我一直在谈论字符串所代表的数字,而不是字符串本身的存储大小。

标签: javascript math hex modular-arithmetic


【解决方案1】:

在最简单的情况下,您可以一次添加一个数字,跟踪进位:

var ndigits = 4, i, carry = 0, d, result = "";
for (i = ndigits - 1; i >= 0; i--) {
  d = parseInt(a[i], 16) + parseInt(b[i], 16) + carry;
  carry = d >> 4;
  result = (d & 15).toString(16) + result;
}

如果性能是一个问题,您可能更愿意一次处理多个数字,但这样事情就会变得困难,或者您必须硬编码位数。即使那样,零填充的东西也需要一些工作。这是一个解决方案,它分三步处理 20 个十六进制数字,因此任何数字的长度都不会超过 32 位:

function pad(s, n) { while (s.length < n) s = "0" + s; return s; }
d = parseInt(a.substr(13), 16) + parseInt(b.substr(13), 16);
result = pad((d & 0xfffffff).toString(16), 7);
d = parseInt(a.substr(6, 7), 16) + parseInt(b.substr(6, 7), 16) + (d >> 28);
result = pad((d & 0xfffffff).toString(16), 7) + result;
d = parseInt(a.substr(0, 6), 16) + parseInt(b.substr(0, 6), 16) + (d >> 28);
result = pad((d & 0xffffff).toString(16), 6) + result;

根据jsPerf,这段代码似乎比上面的快三倍,至少在某些浏览器上是这样。

【讨论】:

  • 如果我使用块或多个字符,我需要如何调整术语 d &gt;&gt; 4d &amp; 15
  • @GoloRoden:取决于块大小:4 是单个十六进制数字的位数,15 是 (1
【解决方案2】:

很高兴看到您已经拥有什么,但您可以使用任意算术库,例如 BigNumber

Javascript

require.config({
    paths: {
        bignumber: 'https://raw.githubusercontent.com/MikeMcl/bignumber.js/master/bignumber.min'
    }
});

require(['bignumber'], function (BigNumber) {
    function sumHex() {
        var args = [].slice.call(arguments),
            length = args.length,
            sum = new BigNumber(0, 16),
            index;

        for (index = 0; index < length; index += 1) {
            sum = sum.plus(args[index], 16).mod('100000000000000000000', 16);
        }

        sum = sum.toString(16);
        while (sum.length < 20) {
            sum = '0' + sum;
        }

        return sum;
    }

    var a = '0000000000000000a3bc',
        b = '00000000000000001d0f';

    console.log(sumHex(a, b));

    a = 'ffffffffffffffffffff';
    b = '00000000000000000001';
    console.log(sumHex(a, b));
});

输出

0000000000000000c0cb
00000000000000000000

开启jsFiddle

【讨论】:

    猜你喜欢
    • 2014-03-07
    • 2012-06-16
    • 2011-11-02
    • 2012-12-06
    • 1970-01-01
    • 1970-01-01
    • 2010-10-04
    • 1970-01-01
    • 2019-07-27
    相关资源
    最近更新 更多