Skip to content
Lesson 7 of 8

Step 1 of 7 · Reading · ~3 min

Learn

Numbers and Performance

BigInt and Numeric Edge Cases

JavaScript Fundamentals introduced the n suffix and showed that Number and BigInt do not mix. This lesson covers what that was hiding: how BigInt rounds, which built-ins reject it, how to force a value into a fixed 64-bit width, and how to move between BigInt, Number and strings without losing digits. By the end you can implement the 64-bit integer arithmetic that hashes, random generators and IDs rely on.

Arithmetic: integers only, and truncating

javascript

Number and BigInt mix only in comparisons: 1n < 2 and 2n == 2 are fine, but 2n === 2 is false because the types differ.

What rejects a BigInt

javascript

The sort case is a real trap: the comparator returns a BigInt (a - b), and sort needs a Number. Compare explicitly, returning -1, 0 or 1:

javascript

Converting without losing digits

Number(big) rounds to the nearest double, so Number(2n ** 64n + 1n) is 18446744073709552000. Parse and print large values as strings. A literal such as 9007199254740993 is rounded when the source code is read, before BigInt is involved; write it as 9007199254740993n or BigInt('9007199254740993'). BigInt('0xff') parses hex, (255n).toString(16) prints it.

Fixed-width integers: asUintN and asIntN

A BigInt never overflows, which is exactly wrong for code that models a 64-bit machine word. BigInt.asUintN(bits, x) keeps the low bits bits and reads them as unsigned; BigInt.asIntN(bits, x) reads the same bits as signed:

javascript

Bit operators work on BigInts: &, |, ^, <<, >> (sign-preserving, so -5n >> 1n is -3n). >>> throws, because with unlimited width there is no sign bit to drop.

A worked case is a 64-bit linear congruential generator. Each step multiplies, adds, and wraps; reading the high bits gives the useful output:

javascript

Without the wrap, state would double in size every step and the generator would be both wrong and slow. Wrapping after every step is the habit to build: it keeps numbers small and is correct for any operation sequence.

Other numeric edges worth knowing

  • Number.isInteger(1n) is false: it checks Numbers only. Use typeof x === 'bigint'.
  • 0n is falsy, like 0.
  • Bitwise operators on Numbers convert to 32-bit signed integers first, so 2 ** 32 | 0 is 0. For 64-bit work, switch to BigInt, not to Number.

Common mistakes

  • Reaching for Number(...) mid-calculation "just for one step". The rounding happens silently and the damage is permanent.
  • Using asIntN when the format is unsigned (hashes, IDs, offsets). Values with the top bit set come out negative.
  • Letting a value grow unbounded and wrapping once at the end. The answer is right but each step gets slower as the number grows.
  • Mixing in a plain Number: hash ^ byte throws if byte is not already a BigInt.

Your exercise

You will implement the 64-bit FNV-1a string hash. The usual slip is treating the hash as a Number or as a signed value, which gives a wrong answer only for inputs whose hash has its top bit set.

Up nextPerformance and V8 OptimizationNumbers and Performance

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…