Convert Big Hex to Big Decimal with JS BigInts
Published 2018-12-11Awesome news! JavaScript now Supports BigInts, natively!!
This has landed in node.js and Chrome, and it's documented on MDN, so Firefox support shouldn't be too far behind.
However, it's not documented very well - not even the basic features that already exist or for non-big numbers - like converting Hex to Decimal and back again.
Convert BigInt Hex to Decimal
There are two ways to go about this:
- Use the BigInt wrapper (which, much like
Number
, doesn't usenew
) - Use the literal BigInt syntax (postfixing a number with
n
)
Although you might expect a BigInt.parseInt
to complement
Number.parseInt(n, 16)
, that's not the case.
Instead you must use base prefixes (0x
for hex).
BigInt('0x102030405060708090a0b0c0d0e0f000').toString(10);
// ↪ "21434780083170533584713300915655864320"
If we break that down a little, we get this:
var hex = '102030405060708090A0B0C0D0E0F000';
var base = 10;
var bn = BigInt('0x' + hex);
// ↪ 21434780083170533584713300915655864320n
bn.toString(base);
// ↪ "21434780083170533584713300915655864320"
You could also use the Hex BigInt literal, but it's (obviously) not as versatile:
0x102030405060708090A0B0C0D0E0F000n.toString(10)
// ↪ "21434780083170533584713300915655864320"
Negative Hex to Dec
BigInts have a sordid past. JavaScript has a sordid past. It's only natural that BigInts in JavaScript have their quirks. Representing negative numbers is one of those tricky parts.
For example, this is obviously an anti-pattern (and wrong):
(-0x102030405060708090A0B0C0D0E0F000n).toString(10)
// ↪ "-21434780083170533584713300915655864320" // WRONG
And this actually gives the wrong type of number:
-0x102030405060708090A0B0C0D0E0F000n.toString(10)
// ↪ -2.1434780083170536e+37 // MORE WRONGER
Traditionally a BigInt will be negative if it's first bit (the "high order bit") is 1 (or 0x80
in hex).
In this scheme, a positive number is designated with a leading 0x00
byte
(which is automatically ignored by the BigInt
wrapper).
So if we want to handle traditional BigInt numbers we'd do like this:
function hexToBn(hex) {
if (hex.length % 2) {
hex = '0' + hex;
}
var highbyte = parseInt(hex.slice(0, 2), 16)
var bn = BigInt('0x' + hex);
if (0x80 & highbyte) {
// bn = ~bn; WRONG in JS (would work in other languages)
// manually perform two's compliment (flip bits, add one)
// (because JS binary operators are incorrect for negatives)
bn = BigInt('0b' + bn.toString(2).split('').map(function (i) {
return '0' === i ? 1 : 0
}).join('')) + BigInt(1);
// add the sign character to output string (bytes are unaffected)
bn = -bn;
}
return bn;
}
hexToBn('7F');
// 127n
hexToBn('80');
// -128n
hexToBn('0080');
// 128n
hexToBn('FF');
// -1n
Need the reverse?
See Convert Decimal strings to Hex with JS BigInts.
Looking for more like this?
Check these out too:
- BigInts land in Chrome and node.js
- Converting between JS BigInts to TypedArrays (includes BigInt64Array and BigUint64Array)
- BigInts and Base64 in JavaScript
Other References
- https://developers.google.com/web/updates/2018/05/bigint
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
- https://coolaj86.com/articles/big-int-encoding/
By AJ ONeal
Did I make your day?
Buy me a coffee
(you can learn about the bigger picture I'm working towards on my patreon page )