Integer Precision Limits in Float64
Learn why JavaScript's Number.MAX_SAFE_INTEGER is 2^53-1 and what happens when you exceed it. Understand the relationship between mantissa bits and integer precision.
Decimal Value
9007199254740992
Float32 Hex
0x5A000000
Float64 Hex
0x4340000000000000
Detailed Explanation
JavaScript and other languages that use float64 as their primary number type can represent integers exactly only up to a certain limit. Beyond that limit, consecutive integers cannot be distinguished.
The safe integer range:
Number.MAX_SAFE_INTEGER = 2^53 - 1 = 9007199254740991
Number.MIN_SAFE_INTEGER = -(2^53 - 1) = -9007199254740991
Why 2^53?
Float64 has 52 mantissa bits plus one implicit leading bit, giving 53 bits of significand precision. An integer that fits in 53 bits can be stored exactly. Integers requiring 54 or more bits must be rounded.
What happens at the boundary:
9007199254740991 + 1 // 9007199254740992 ✓ (exact, it's 2^53)
9007199254740992 + 1 // 9007199254740992 ✗ (should be ...993, but rounds to ...992)
9007199254740992 + 2 // 9007199254740994 ✓ (even numbers at this magnitude are exact)
9007199254740993 === 9007199254740992 // true! (they cannot be distinguished)
At 2^53, the ULP (gap between representable values) becomes 2. So only even integers are representable. At 2^54, the gap becomes 4, and only multiples of 4 are exact.
Practical consequences:
Database IDs: IDs from databases (like Twitter's snowflake IDs) that exceed 2^53 lose precision when parsed as JavaScript numbers. This is why APIs often return large IDs as strings.
Financial calculations:
0.01(1 cent) cannot be represented exactly in float64. Working with cents as integers avoids this, but only if the integer does not exceed MAX_SAFE_INTEGER.Bitwise operations: JavaScript's bitwise operators (
|,&,^,~,<<,>>) work on 32-bit integers, truncating any higher bits.(2**53) | 0gives0.
Solutions:
- BigInt (JavaScript):
9007199254740993n + 1nworks correctly - Long (Java): 64-bit integer with range up to
2^63 - 1 - Decimal libraries: for exact decimal arithmetic
- String representation: pass large IDs as strings in JSON
Testing for safe integers:
Number.isSafeInteger(9007199254740991) // true
Number.isSafeInteger(9007199254740992) // false (2^53 is not safe)
Number.isSafeInteger(1.5) // false (not an integer)
Use Case
Understanding integer precision limits is crucial for web developers working with APIs that return large numeric IDs, financial applications performing arithmetic on currency values, and anyone porting code between languages with different integer and floating-point representations.