13

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

 var largeNumber = '11010011010110100001010011111010010111011111000010010111000111110011111011111000001100000110000011000001100111010100111010101110100010001011010101110011110000011000001100000110000011001001100000110000011000001100000110000111000011100000110000011000001100000110000011000010101100011001110101101001100110100100000110000011000001100000110001001101011110110010001011010001101011010100011001001110001110010100111011011111010000110001110010101010001111010010000101100001000001100001011000011011111000011110001110111110011111111000100011110110101000101100000110000011000001100000110000011010011101010110101101001111101001010010111101011000011101100110010011001001111101'

When I convert it to decimal by use of parseInt(largeNumber, 10)l it gives me 1.5798770299367407e+199 but when I try to convert it back to binary:

parseInt(`1.5798770299367407e+199`, 2)

it returns 1 (which I think is related to how parseInt works by rounding value) when I was expecting to see my original binary representation of largeNumber. Can you explain me such behavior? And how I can convert it back to original state in JavaScript?

EDIT: This question is a result of my experiment where I was playing around with storing and transferring large amount of boolean data. The largeNumber is a representation of a collection [true,true,false,true ...] of boolean values which has to be shared between client, client worker and server.

LJ Wadowski
  • 6,424
  • 11
  • 43
  • 76
  • 1
    `parseInt` isn't a method of converting from one number system to another. You need to go read what it _actually_ does, then rethink how you achieve your end result. – James Thorpe Sep 05 '16 at 16:29
  • _"I have a very large number represented as binary"_ How was original number converted to `largeNumber`? – guest271314 Sep 05 '16 at 17:28
  • 3
    Converting a numerical string to a number is not a lossless process. Numbers only have 64 bits, they can't store much information. – Oriol Sep 05 '16 at 17:37
  • @LJWadowski Can you include procedure which results in `largeNumber` at Question? – guest271314 Sep 05 '16 at 18:16
  • 1
    This is a collection of boolean values which is a result of my experiment. I was trying to store the array of boolean as a binary number and convert them to decimal number to see if I can do that process more memory efficient – LJ Wadowski Sep 05 '16 at 18:25
  • Yes that is one of proposes, the second one is that I'm trying to find efficient way to transfer such information between server and client. – LJ Wadowski Sep 05 '16 at 19:25
  • @LJWadowski Interesting approach. Have you tried using `Blob` or `ArrayBuffer` to store and transfer data? – guest271314 Sep 05 '16 at 19:27
  • Yes I have played with ArrayBuffer but hadn't try blob yet – LJ Wadowski Sep 05 '16 at 19:29
  • 1
    Have you considered included your description of actual requirement and expected result at Question? It may be helpful in resolving actual requirement? Was not certain what you were actually trying to achieve before reading your comment at http://stackoverflow.com/questions/39334494/converting-large-numbers-from-binary-to-decimal-and-back-to-binary-in-javascript#comment66003186_39334494 – guest271314 Sep 05 '16 at 19:35
  • @LJWadowski See [Convert exponential number to a whole number in javascript](http://stackoverflow.com/questions/14037684/convert-exponential-number-to-a-whole-number-in-javascript) – guest271314 Sep 05 '16 at 19:58
  • The number is way too large to convert to a binary number. JS only allows up to 2^53 - 1, so the answer you receive is imprecise. – Andrew Li Sep 05 '16 at 20:01
  • That's all useful to understand that process better, it seems like I need to store those information as 64bit chunks. I'm also wondering what will happen if I let say store that information in canvas element, will it use GPUs memory? Anyway that's subject for another discussion. Thanks for help. – LJ Wadowski Sep 05 '16 at 20:05
  • @LJWadowski Another approach could be to stream the data in chunks? – guest271314 Sep 05 '16 at 20:06
  • @guest271314 yep, this looks like the best solution – LJ Wadowski Sep 05 '16 at 20:08
  • @LJWadowski Though, granted, that would not reduce length of streamed data from `662` to `23`. See also [Method for streaming data from browser to server via HTTP](http://stackoverflow.com/questions/35899536/method-for-streaming-data-from-browser-to-server-via-http/) – guest271314 Sep 05 '16 at 20:09
  • @Oriol: Given that we're dealing with integers here, there's only 53 bits of storage before losing precision. – Bergi Sep 05 '16 at 20:12
  • BTW, `parseInt('1.5798770299367407e+199', 2)` does not convert back to binary. The code `(1.5798770299367407e+199).toString(2)` does, but precision limits apply (53 bits). – Tomas Langkaas Nov 01 '21 at 21:07

4 Answers4

12

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);
Community
  • 1
  • 1
Tomas Langkaas
  • 4,551
  • 2
  • 19
  • 34
11

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';
        }
    });
6

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
Farzher
  • 13,934
  • 21
  • 69
  • 100
5

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.

Andrew Li
  • 55,805
  • 14
  • 125
  • 143