Overview

3 Numbers

JavaScript numbers look simple on the surface, but the chapter shows that they rest on a surprisingly intricate foundation. The language has only two numeric types, Number and BigInt, and the familiar problem of 0.1 + 0.2 exposing 0.30000000000000004 comes from IEEE 754 floating-point encoding in binary rather than from a flaw in JavaScript itself. To explain this, the text walks through how values are turned into bits, how binary and two’s complement represent integers, and how floating-point uses a sign, exponent, and significand to cover a huge range of values while sacrificing exact decimal precision.

The chapter also digs into the practical consequences of that design. It explains why floating-point values are only approximate, how rounding errors accumulate, and why Number.EPSILON can help compare numbers that are “close enough” while still needing to be scaled for the magnitude of the values involved. It highlights special cases such as Infinity, negative infinity, and NaN, and shows where Number reaches its limits, especially around the maximum safe integer, beyond which some integers cannot be represented exactly. Because of these limits, the text positions BigInt as the solution for exact whole-number arithmetic when values exceed the safe range or when correctness matters more than speed.

Finally, the chapter looks at how JavaScript engines optimize numeric performance behind the scenes, especially in V8. Small integers can be stored as SMIs, avoiding heap allocation and making common arithmetic faster, while larger or non-integer values become HeapNumbers. The discussion emphasizes that consistency matters: keeping loop counters and array indices as integers, avoiding unnecessary floating-point math, and not mixing numeric representations can help the engine stay on its fast path. The chapter closes by pointing toward a proposed Decimal type, designed to provide exact base-10 arithmetic for financial and other decimal-heavy work, giving JavaScript a more complete numeric toolbox for the future.

Unsigned and signed binary layout consists of any number of bits. In the unsigned variant, every bit is used to represent the value, whereas in the signed variant, the most significant bit is reserved as representing the sign and the remaining bits represent the value.
The “double-precision” floating point layout divides the 64 available bits into distinct components, including a sign-bit, eleven exponent bits, and 52 bits for the significand.

Summary

  • Numbers are 64-bit floating-point values (Binary64) that cannot precisely represent decimal fractions like 0.1 or 0.2.
  • Binary encoding causes 0.1 + 0.2 to equal 0.30000000000000004 due to infinite repeating patterns in base-2 representation.
  • 2's complement encoding enables signed integers by inverting bits and adding 1, allowing the same arithmetic circuits for positive and negative numbers.
  • Floating-point trades range for precision: 53 bits of accuracy regardless of magnitude, from 5e-324 to 1.7976931348623157e+308.
  • The maximum safe integer is 2^53 - 1 (9,007,199,254,740,991), beyond which not all integers can be represented exactly.
  • Number.EPSILON provides the smallest representable difference for comparing floating-point values that should be equal but aren't due to rounding.
  • BigInt uses dynamic memory allocation to represent arbitrarily large integers without precision loss, at 10-200x performance cost.
  • JavaScript forbids mixing BigInt and Number in arithmetic to prevent accidental precision loss, though comparisons work.
  • The proposed Decimal type will use base-10 encoding to enable exact decimal arithmetic for financial and scientific calculations.
  • V8 optimizes integers between -2^31 and 2^31-1 as SMIs, storing them directly without heap allocation for better performance.
  • Consistent numeric types in arrays and loops help V8 maintain optimizations, while mixing types forces slower generic handling.

FAQ

Why does 0.1 + 0.2 not equal 0.3 in JavaScript?JavaScript uses IEEE 754 double-precision floating-point numbers, which represent decimal fractions in binary. Values like 0.1 and 0.2 cannot be stored exactly, so tiny rounding errors accumulate and the sum becomes 0.30000000000000004 instead of 0.3.
What numeric types does JavaScript provide?JavaScript has two built-in numeric types: Number and BigInt. Number is used for general-purpose arithmetic and floating-point values, while BigInt is used for arbitrarily large integers that must remain exact.
What is the difference between binary representation and 2’s complement?Binary representation stores whole numbers using bits as powers of 2. 2’s complement is a signed binary format that uses the most significant bit as a sign bit, allowing both positive and negative integers to be represented and added with the same arithmetic rules.
What is the maximum safe integer in JavaScript?The maximum safe integer is 253 - 1, which is 9,007,199,254,740,991. Beyond this limit, JavaScript Numbers can no longer represent every integer exactly, so values may become corrupted or indistinguishable from nearby integers.
How should floating-point values be compared safely?Use a tolerance instead of strict equality. A common approach is to compare the absolute difference between two numbers against Number.EPSILON, or better, scale that epsilon based on the size of the numbers being compared.
What is Number.EPSILON?Number.EPSILON is the smallest difference between two representable Binary64 numbers, equal to 2-52 (about 2.220446049250313e-16). It is useful for checking whether two floating-point values are close enough to be considered equal.
When should I use BigInt instead of Number?Use BigInt when exact integer arithmetic matters and values may exceed the safe integer range, such as for finance, cryptography, IDs, or 64-bit integer interop. Use Number when values stay within the safe range or when performance is more important than arbitrary precision.
Why can’t BigInt and Number be mixed in arithmetic?JavaScript forbids mixing BigInt and Number in arithmetic to prevent accidental precision loss. If you need to combine them, you must explicitly convert one side, such as with BigInt(5) or Number(10n).
What is the proposed Decimal type for?The proposed Decimal type is intended to support exact decimal arithmetic, especially for money and other values where base-10 precision matters. It would avoid the binary rounding issues that affect Numbers and provide more accurate decimal calculations.
What is V8’s SMI optimization?SMI stands for Small Integer. V8 stores small integers directly in a fast internal representation instead of allocating a heap object, which can make integer-heavy code faster and use less memory. If values switch to decimals or exceed the SMI range, V8 falls back to HeapNumber storage.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • JavaScript in Depth ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • JavaScript in Depth ebook for free