【问题标题】:Converting large numbers from binary to decimal and back in JavaScript在 JavaScript 中将大数从二进制转换为十进制并返回
【发布时间】:2017-01-13 01:18:47
【问题描述】:

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

 var largeNumber = '11010011010110100001010011111010010111011111000010010111000111110011111011111000001100000110000011000001100111010100111010101110100010001011010101110011110000011000001100000110000011001001100000110000011000001100000110000111000011100000110000011000001100000110000011000010101100011001110101101001100110100100000110000011000001100000110001001101011110110010001011010001101011010100011001001110001110010100111011011111010000110001110010101010001111010010000101100001000001100001011000011011111000011110001110111110011111111000100011110110101000101100000110000011000001100000110000011010011101010110101101001111101001010010111101011000011101100110010011001001111101'

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

parseInt(`1.5798770299367407e+199`, 2)

当我期待看到largeNumber 的原始二进制表示时,它返回1(我认为这与parseInt 如何通过舍入值工作有关)。你能解释一下这种行为吗?以及如何在 JavaScript 中将其转换回原始状态?

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

【问题讨论】:

  • parseInt 不是从一个数字系统转换为另一个数字系统的方法。你需要去阅读它实际做了什么,然后重新思考你如何实现你的最终结果。
  • “我有一个非常大的数字表示为二进制” 原始数字如何转换为largeNumber
  • 将数字字符串转换为数字不是无损过程。数字只有 64 位,不能存储太多信息。
  • 这是一个布尔值的集合,它是我实验的结果。我试图将布尔数组存储为二进制数并将它们转换为十进制数,以查看是否可以执行该过程以提高内存效率
  • 您是否考虑在问题中包含您对实际需求和预期结果的描述?它可能有助于解决实际需求?在阅读您在stackoverflow.com/questions/39334494/… 的评论之前,不确定您实际上想要实现什么

标签: javascript


【解决方案1】:

如果您希望传输大量二进制数据,则应使用 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';
        }
    });

【讨论】:

    【解决方案2】:

    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
    

    【讨论】:

    • 这非常适合我的需要。我只有一个大的二进制字符串,我必须转换为十进制字符串
    • 请注意,使用BigInt,可以使用单个表达式BigInt('0b' + str)解决OP。
    【解决方案3】:

    正如Andrew L.'s answer 和几位评论者所指出的,您的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);

    【讨论】:

      【解决方案4】:

      当您将其转换回二进制时,您不会将其解析为基数 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

      【讨论】:

      • largeNumber 太大了。
      • 这里的另一个微妙之处是负数:stackoverflow.com/questions/9939760/…
      • @IanGilroy 是的,这是另一件需要考虑的事情
      • @guest271314 它没有。这是因为二进制数对于JS来说太大了,看答案的最后部分
      • "告诉 JS 将基数 10 解析为基数 2!" - 实际上比这更糟糕。他告诉要以科学计数法解析浮点数作为整数
      猜你喜欢
      • 1970-01-01
      • 2016-12-25
      • 2020-03-28
      • 2013-05-28
      • 1970-01-01
      • 2017-12-16
      • 2015-09-06
      • 2020-04-28
      • 1970-01-01
      相关资源
      最近更新 更多