在 JavaScript 中将大数从二进制转换为十进制并返回

IT技术 javascript
2021-02-20 20:45:33

我有一个非常大的数字在 JavaScript 中表示为二进制:

 var largeNumber = '11010011010110100001010011111010010111011111000010010111000111110011111011111000001100000110000011000001100111010100111010101110100010001011010101110011110000011000001100000110000011001001100000110000011000001100000110000111000011100000110000011000001100000110000011000010101100011001110101101001100110100100000110000011000001100000110001001101011110110010001011010001101011010100011001001110001110010100111011011111010000110001110010101010001111010010000101100001000001100001011000011011111000011110001110111110011111111000100011110110101000101100000110000011000001100000110000011010011101010110101101001111101001010010111101011000011101100110010011001001111101'

当我使用parseInt(largeNumber, 10)l将其转换为十进制时,它给了我,1.5798770299367407e+199但是当我尝试将其转换回二进制时:

parseInt(`1.5798770299367407e+199`, 2)

它返回1(我认为这与parseInt舍入值的工作方式有关)当我期望看到largeNumber. 你能解释我这种行为吗?以及如何在 JavaScript 中将其转换回原始状态?

编辑:这个问题是我在尝试存储和传输大量布尔数据的实验的结果。largeNumber[true,true,false,true ...]必须在客户端、客户端工作器和服务器之间共享的布尔值集合表示

4个回答

正如Andrew L. 的回答以及一些评论者所指出的那样,您largeNumber超出了 JavaScript 可以在不损失精度的情况下表示为普通数字中的整数 - 即 9.007199254740991e+15

如果你想处理更大的整数,你需要一个 BigInt 库或其他特殊用途的代码。

下面是一些代码,演示如何在不同的基本表示之间转换任意大的正整数,显示您的精确十进制表示largeNumber

15 798 770 299 367 407 029 725 345 423 297 491 683 306 908 462 684 165 669 735 033 278 996 876 231 474 309 788 453 071 122 111 686 268 816 862 247 538 905 966 252 886 886 438 931 450 432 740 640 141 331 094 589 505 960 171 298 398 097 197 475 262 433 234 991 526 525

function parseBigInt(bigint, base) {
  //convert bigint string to array of digit values
  for (var values = [], i = 0; i < bigint.length; i++) {
    values[i] = parseInt(bigint.charAt(i), base);
  }
  return values;
}

function formatBigInt(values, base) {
  //convert array of digit values to bigint string
  for (var bigint = '', i = 0; i < values.length; i++) {
    bigint += values[i].toString(base);
  }
  return bigint;
}

function convertBase(bigint, inputBase, outputBase) {
  //takes a bigint string and converts to different base
  var inputValues = parseBigInt(bigint, inputBase),
    outputValues = [], //output array, little-endian/lsd order
    remainder,
    len = inputValues.length,
    pos = 0,
    i;
  while (pos < len) { //while digits left in input array
    remainder = 0; //set remainder to 0
    for (i = pos; i < len; i++) {
      //long integer division of input values divided by output base
      //remainder is added to output array
      remainder = inputValues[i] + remainder * inputBase;
      inputValues[i] = Math.floor(remainder / outputBase);
      remainder -= inputValues[i] * outputBase;
      if (inputValues[i] == 0 && i == pos) {
        pos++;
      }
    }
    outputValues.push(remainder);
  }
  outputValues.reverse(); //transform to big-endian/msd order
  return formatBigInt(outputValues, outputBase);
}

var largeNumber =
  '1101001101011010000101001111101001011101' + 
  '1111000010010111000111110011111011111000' +
  '0011000001100000110000011001110101001110' +
  '1010111010001000101101010111001111000001' +
  '1000001100000110000011001001100000110000' +
  '0110000011000001100001110000111000001100' +
  '0001100000110000011000001100001010110001' +
  '1001110101101001100110100100000110000011' +
  '0000011000001100010011010111101100100010' +
  '1101000110101101010001100100111000111001' +
  '0100111011011111010000110001110010101010' +
  '0011110100100001011000010000011000010110' +
  '0001101111100001111000111011111001111111' +
  '1000100011110110101000101100000110000011' +
  '0000011000001100000110100111010101101011' +
  '0100111110100101001011110101100001110110' +
  '0110010011001001111101';

//convert largeNumber from base 2 to base 10
var largeIntDecimal = convertBase(largeNumber, 2, 10);


function groupDigits(bigint){//3-digit grouping
  return bigint.replace(/(\d)(?=(\d{3})+$)/g, "$1 ");
}

//show decimal result in console:
console.log(groupDigits(largeIntDecimal));

//converting back to base 2:
var restoredOriginal = convertBase(largeIntDecimal, 10, 2);

//check that it matches the original:
console.log(restoredOriginal === largeNumber);

如果您要传输大量二进制数据,则应使用 BigInt。BigInt 允许您表示任意数量的位。

// parse large number from string
let numString = '1101001101011010000101001111101001011101111100001001'

// as number
let num = BigInt('0b' + numString)

// now num holds large number equivalent to numString
console.log(num)  // 3718141639515913n

// print as base 2
console.log(num.toString(2))  // 1101001101011010000101001111101001011101111100001001

辅助函数

// some helper functions

// get kth bit from right
function getKthBit(x, k){
  return (x & (1n << k)) >> k;
}

// set kth bit from right to 1
function setKthBit(x, k){
  return (1n << k) | x;
}

// set kth bit from right to 0
function unsetKthBit(x, k){
  return (x & ~(1n << k));
}

getKthBit(num, 0n);  
// 1n

getKthBit(num, 5n);  
// 0n

setKthBit(num, 1n).toString(2); 
// 1101001101011010000101001111101001011101111100001011

setKthBit(num, 4n); 
// 1101001101011010000101001111101001011101111100011001

unsetKthBit(num, 0n).toString(2);
// 1101001101011010000101001111101001011101111100001000

unsetKthBit(num, 0n).toString(2);
// 1101001101011010000101001111101001011101111100000001

为方便起见,如果您要序列化回客户端,您可能希望将此添加到 BigInt。然后您可以将其作为字符串读回。否则你会得到“Uncaught TypeError: Do not know how to serialize a BigInt”因为出于某种原因Javascript Object Notation不知道如何序列化Javascript中的一种类型。

    Object.defineProperty(BigInt.prototype, "toJSON", {
        get() {
            "use strict";
            return () => this.toString() + 'n';
        }
    });

BigInt 内置于 js

function parseBigInt(str, base=10) {
  base = BigInt(base)
  var bigint = BigInt(0)
  for (var i = 0; i < str.length; i++) {
    var code = str[str.length-1-i].charCodeAt(0) - 48; if(code >= 10) code -= 39
    bigint += base**BigInt(i) * BigInt(code)
  }
  return bigint
}
parseBigInt('11010011010110100001010011111010010111011111000010010111000111110011111011111000001100000110000011000001100111010100111010101110100010001011010101110011110000011000001100000110000011001001100000110000011000001100000110000111000011100000110000011000001100000110000011000010101100011001110101101001100110100100000110000011000001100000110001001101011110110010001011010001101011010100011001001110001110010100111011011111010000110001110010101010001111010010000101100001000001100001011000011011111000011110001110111110011111111000100011110110101000101100000110000011000001100000110000011010011101010110101101001111101001010010111101011000011101100110010011001001111101', 2)
// 15798770299367407029725345423297491683306908462684165669735033278996876231474309788453071122111686268816862247538905966252886886438931450432740640141331094589505960171298398097197475262433234991526525n
这非常适合我的需要。我只有一个很大的二进制字符串,我必须转换为十进制字符串
2021-04-17 20:45:33
请注意,BigInt可以使用单个表达式解决 OP BigInt('0b' + str)
2021-04-27 20:45:33

当您将其转换回二进制时,您不会将其解析为基数 2,这是错误的。您还试图将整数解析为浮点数,这可能会导致不精确。有了这条线:

parseInt(`1.5798770299367407e+199`, 2)

您是在告诉 JS 将基数 10 解析为基数 2!您需要做的是像这样将其转换为二进制(注意使用parseFloat):

var largeNumber = '11010011010110100001010011111010010111011111000010010111000111110011111011111000001100000110000011000001100111010100111010101110100010001011010101110011110000011000001100000110000011001001100000110000011000001100000110000111000011100000110000011000001100000110000011000010101100011001110101101001100110100100000110000011000001100000110001001101011110110010001011010001101011010100011001001110001110010100111011011111010000110001110010101010001111010010000101100001000001100001011000011011111000011110001110111110011111111000100011110110101000101100000110000011000001100000110000011010011101010110101101001111101001010010111101011000011101100110010011001001111101';

//intLN is integer of large number
var intLN = parseFloat(largeNumber, 2); //here, you used base 10 to parse as integer, Incorrect
console.log(intLN);

var largeNumberConvert = intLN.toString(2); //here, we convert back to binary with toString(radix).
console.log(largeNumberConvert);

之前,您将十进制转换为二进制。您需要做的是调用toString(radix)将其转换回二进制,因此:

var binaryRepresentation = integerFormOfLargeNumber.toString(2);

如果您查看输出,您会看到:

Infinity
Infinity

由于您的二进制数非常大,它会影响结果。因为 JS 最多支持 64 位,所以这个数字太大了。它导致Infinity并且不精确。如果您尝试将largeNumberConvert二进制重新转换为十进制,如下所示:

parseInt(largeNumberConvert, 10);

你可以看到它输出Infinity.

@IanGilroy 是的,这是另一件需要考虑的事情
2021-04-28 20:45:33
这里的另一个微妙之处是负数:stackoverflow.com/questions/9939760/...
2021-05-03 20:45:33
@guest271314 没有。这是因为二进制数对于 JS 来说太大了,请参阅答案的最后一部分
2021-05-11 20:45:33
告诉 JS 将基数 10 解析为基数 2! ” - 实际上比这更糟糕。他告诉将科学记数法中的浮点数解析为 integer
2021-05-13 20:45:33
largeNumber 对于这个来说太大了。
2021-05-15 20:45:33