21

Suppose I want to convert a base-36 encoded string to a BigInt, I can do this:

BigInt(parseInt(x,36))

But what if my string exceeds what can safely fit in a Number? e.g.

parseInt('zzzzzzzzzzzzz',36)

Then I start losing precision.

Are there any methods for parsing directly into a BigInt?

mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • 1
    At the time of writing, the answer is no: there is no mention of any equivalent of `parseInt` in the [BigInt proposal](https://tc39.github.io/proposal-bigint/) (Stage 3 Draft, February 11 2019). The only conversion from a string mentioned in the proposal is the `BigInt()` function itself, but that only covers bases 2, 8, 10 and 16. – Thomas Apr 12 '19 at 08:00
  • 2
    @Thomas https://github.com/tc39/proposal-number-fromstring – Yukulélé Oct 07 '21 at 18:10

2 Answers2

14

You could convert the number to a bigint type.

function convert(value, radix) {
    return [...value.toString()]
        .reduce((r, v) => r * BigInt(radix) + BigInt(parseInt(v, radix)), 0n);
}

console.log(convert('zzzzzzzzzzzzz', 36).toString());

With greater chunks, like just for example with ten (eleven return a false result).

function convert(value, radix) { // value: string
    var size = 10,
        factor = BigInt(radix ** size),
        i = value.length % size || size,
        parts = [value.slice(0, i)];

    while (i < value.length) parts.push(value.slice(i, i += size));

    return parts.reduce((r, v) => r * factor + BigInt(parseInt(v, radix)), 0n);
}

console.log(convert('zzzzzzzzzzzzz', 36).toString());
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Very nice. I didn't know that the parseInt function can work with radices > 16 too. What about radices > 36? Is this documented somewhere? (Didn't find it on MDN...) – Bart Hofland Apr 12 '19 at 08:05
  • Never mind my questions. MDN states it accepts radices from 2 to 36. ;) – Bart Hofland Apr 12 '19 at 08:06
  • for greater radices, you need a custom character set, and an own function. – Nina Scholz Apr 12 '19 at 08:08
  • 1
    I wonder if we can batch this in chunks of 11 chars? (`Number.MAX_SAFE_INTEGER.toString(36).length`).. err, 10 I guess. – mpen Apr 12 '19 at 22:19
5

Not sure if there's a built-in one, but base-X to BigInt is pretty easy to implement:

function parseBigInt(
  numberString,
  keyspace = "0123456789abcdefghijklmnopqrstuvwxyz",
) {
  let result = 0n;
  const keyspaceLength = BigInt(keyspace.length);
  for (let i = 0; i < numberString.length; i++) {
    const value = keyspace.indexOf(numberString[i]);
    if (value === -1) throw new Error("invalid string");
    result = result * keyspaceLength + BigInt(value);
  }
  return result;
}

console.log(parseInt("zzzzzzz", 36));
console.log(parseBigInt("zzzzzzz"));
console.log(parseBigInt("zzzzzzzzzzzzzzzzzzzzzzzzzz"));

outputs

78364164095
78364164095n
29098125988731506183153025616435306561535n

The default keyspace there is equivalent to what parseInt with base 36 uses, but should you need something else, the option's there. :)

lusc
  • 1,206
  • 1
  • 10
  • 19
AKX
  • 152,115
  • 15
  • 115
  • 172
  • This function doesn't produce correct results for any string value ending in `0`? – Zak Henry Dec 02 '21 at 00:01
  • This parses numbers incorrectly. `parseBigInt('xyz').toString(36)` returns `zyx`. The problem is that it iterates over the string in reverse, parsing the number in reverse. – lusc Jul 30 '23 at 20:31
  • @lusc I'm pretty sure this implementation does what it should – it's iterating in reverse because of the `result = result * number-of-digits + current-digit` operation... – AKX Jul 31 '23 at 08:30
  • Have you tried doing `parseBigInt('xyz').toString(36)`? Unless I'm missing something it should not return `zyx`. – lusc Jul 31 '23 at 08:48
  • @lusc Huh, yeah. Wonder what I'd been thinking, and wonder how this remained unnoticed for a good four years. Thanks! – AKX Jul 31 '23 at 09:38