Converting large numbers from binary to decimal and back in JavaScript

后端 未结 4 1909
感动是毒
感动是毒 2020-11-29 11:54

I have a very large number represented as binary in JavaScript:

 var largeNumber = \'110100110101101000010100111110100101110111110000100101110001111100111110         


        
相关标签:
4条回答
  • 2020-11-29 12:10

    If you're looking to transfer a large amount of binary data, you should use BigInt. BigInt allows you to represent an arbitrary number of bits.

    // 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
    

    Helper functions

    // 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
    

    For convenience you may want to add this to BigInt if you're going to be serializing back to the client. Then you can read it back as a string. Otherwise you will get "Uncaught TypeError: Do not know how to serialize a BigInt" because for some reason Javascript Object Notation doesn't know how to serialize one of the types in Javascript.

        Object.defineProperty(BigInt.prototype, "toJSON", {
            get() {
                "use strict";
                return () => this.toString() + 'n';
            }
        });
    
    0 讨论(0)
  • 2020-11-29 12:22

    BigInt is built into 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
    
    0 讨论(0)
  • 2020-11-29 12:30

    When you convert it back to binary, you don't parse it as base 2, that's wrong. You're also trying to parse an integer as a float, this can cause imprecision. With this line:

    parseInt(`1.5798770299367407e+199`, 2)
    

    You're telling JS to parse a base 10 as base 2! What you need to do is convert it to binary like so (note the use of 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);

    Before, you converted a decimal to binary. What you need to do is call toString(radix) to convert it back into binary, so:

    var binaryRepresentation = integerFormOfLargeNumber.toString(2);
    

    If you look at the output, you see:

    Infinity
    Infinity
    

    Since your binary number is quite large, it can affect the results. Because JS supports up to 64 bits, the number is way too large. It causes Infinity and is imprecise. If you try re-converting the largeNumberConvert from binary to decimal like this:

    parseInt(largeNumberConvert, 10);
    

    You can see that it outputs Infinity.

    0 讨论(0)
  • 2020-11-29 12:32

    As noted in Andrew L.'s answer, and by several commenters, your largeNumber exceeds what JavaScript can represent as an integer in an ordinary number without loss of precision—which is 9.007199254740991e+15.

    If you want to work with larger integers, you will need a BigInt library or other special-purpose code.

    Below is some code demonstrating how to convert arbitrarily large positive integers between different base representations, showing that the exact decimal representation of your largeNumber is

    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);

    0 讨论(0)
提交回复
热议问题