BigInteger到Uint8Array的字节

时间:2018-01-30 12:45:35

标签: javascript arrays biginteger

我需要在JavaScript中获取一个大整数的字节。

我尝试了几个大整数库,但实际提供此功能的那个库不起作用。

我不太确定如何自己实现这个,给定一个包含大量数字的字符串,这通常是库提供访问权限。

是否有可以使用的库并且可以执行此操作? 或者它实际上并不难,我只是缺少一些东西?

2 个答案:

答案 0 :(得分:0)

我一直在寻找JavaScript中快速而优雅的解决方案,但我发现的唯一方法是基于中间十六进制字符串的转换方法。肯定是次优的,不幸的是,该代码也不适用于我。因此,我实现了自己的代码,并希望将其发布为我自己的问题的答案,但是找到了这个代码。

说明

首先,我将回答相反的问题,因为它更具说明性。

从字节数组读取BigInteger

什么是字节数组?这是256基数字系统中的数字,我们希望将其转换为10基(十进制)系统更方便。 例如,让我们获取一个字节数组
[AA] [BB] [CC] [DD] (1个字节为8位或2个十六进制数字)。

根据我们从哪方面开始(参见https://en.wikipedia.org/wiki/Endianness),我们可以将其读取为:

  • (li * -endian
  • 中的(AA * 1 + BB * 256 + CC * 256 ^ 2 + DD * 256 ^ 3)
  • big-endian 中的(DD * 1 + CC * 256 + BB * 256 ^ 2 + AA * 256 ^ 3)。

在这里使用 little-endian 。因此,由数组 [AA] [BB] [CC] [DD] 编码的数字是:

AA + BB*256 + CC*256^2 + DD*256^3  
= 170 + 187*256 + 204*65536 + 221*16777216  
= 170 + 47872 + 13369344 + 3707764736  
= 3721182122

将BigInteger写入字节数组

要将数字写到字节数组中,我们必须执行相反的操作,即在十进制系统中有一个数字才能在256基数字系统中找到它的所有数字。让我们使用相同的数字: 3721182122

要找到它的最低有效字节(https://en.wikipedia.org/wiki/Bit_numbering#Least_significant_byte),我们必须将其除以256。其余部分代表更高的数字。因此,我们将余数再除以256,以此类推,直到收到0余数:

3721182122 = 14535867*256 + 170  
14535867 = 56780*256 + 187  
56780 = 221*256 + 204  
221 = 0*256 + 221  

因此,结果为十进制[170] [187] [204] [221],十六进制为 [AA] [BB] [CC] [DD]

JavaScript解决方案

现在,这是使用NodeJS库在big-integer中进行编码的算法。

const BigInteger = require('big-integer');

const zero = BigInteger(0);
const one = BigInteger(1);
const n256 = BigInteger(256);

function fromLittleEndian(bytes) {
    let result = zero;
    let base = one;
    bytes.forEach(function (byte) {
        result = result.add(base.multiply(BigInteger(byte)));
        base = base.multiply(n256);
    });
    return result;
}

function fromBigEndian(bytes) {
  return fromLittleEndian(bytes.reverse());
}

function toLittleEndian(bigNumber) {
    let result = new Uint8Array(32);
    let i = 0;
    while (bigNumber.greater(zero)) {
        result[i] = bigNumber.mod(n256);
        bigNumber = bigNumber.divide(n256);
        i += 1;
    }
    return result;
}

function toBigEndian(bytes) {
  return toLittleEndian(bytes).reverse();
}

console.log('Reading BigInteger from an array of bytes');
let bigInt = fromLittleEndian(new Uint8Array([170, 187, 204, 221]));
console.log(bigInt.toString());

console.log('Writing BigInteger to an array of bytes');
let bytes = toLittleEndian(bigInt);
console.log(bytes);

基准

我为此方法编写了小型基准。欢迎任何人为自己的转换方法进行修改,并与我的方法进行比较。

https://repl.it/repls/EvenSturdyEquipment

答案 1 :(得分:0)

我有一个与BigInt兼容的版本,该版本受浏览器支持:

const big0 = BigInt(0)
const big1 = BigInt(1)
const big8 = BigInt(8)


  bigToUint8Array(big: bigint) {
    if (big < big0) {
      const bits: bigint = (BigInt(big.toString(2).length) / big8 + big1) * big8
      const prefix1: bigint = big1 << bits
      big += prefix1
    }
    let hex = big.toString(16)
    if (hex.length % 2) {
      hex = '0' + hex
    }
    const len = hex.length / 2
    const u8 = new Uint8Array(len)
    var i = 0
    var j = 0
    while (i < len) {
      u8[i] = parseInt(hex.slice(j, j + 2), 16)
      i += 1
      j += 2
    }
    return u8
  }

我有一个BigDecimal实现,可以将发送和接收字节作为任意精度的大十进制:https://jackieli.dev/posts/bigint-to-uint8array/