chapter three

3 Numbers

 

This chapter covers

  • Exploring why 0.1 + 0.2 doesn't equal 0.3 in floating-point
  • Encoding integers with binary and 2's complement
  • Comparing floating-point values safely with Number.EPSILON
  • Working with BigInt beyond the safe integer limit
  • Previewing the proposed Decimal type for exact decimals
  • Examining V8's SMI optimization for integer performance

In Chapter 2, we discovered that strings, despite seeming simple, hide surprising complexity in their encoding and implementation. Numbers in JavaScript present an equally deceptive simplicity. While most programming languages make you choose between integers and decimals, signed and unsigned, 8-bit and 64-bit, JavaScript offers just two numeric types: Number and BigInt.

Consider this seemingly basic arithmetic:

console.log(0.1 + 0.2);  // 0.30000000000000004
console.log(0.3);        // 0.3
console.log(0.1 + 0.2 === 0.3);  // false

This result bewilders newcomers and has spawned countless memes, but it's not a bug. It's a direct consequence of how JavaScript chose to represent all numbers as double-precision floating-point values. This single decision shapes everything from why your financial calculations might be off by a penny, to why counting past 9,007,199,254,740,991 becomes unreliable, to why V8 secretly treats small integers completely differently than decimals.

3.2 What is a number

3.2.1 How numbers are encoded using bytes

3.3 What is a BigInt

3.3.1 Bigint limitations

3.3.2 BigInts vs. Numbers

3.4 Introducing Decimals

3.5 How V8 implements numbers

3.5.1 SMI: The small integer fast path

3.5.2 The number representation hierarchy

3.5.3 Performance optimization strategies

3.6 Summary