C.W.K.
Stream
Lesson 04 of 08 · published

Numbers — IEEE 754 With Caveats

~10 min · json, numbers, ieee754, precision

Level 0Plaintext
0 XP0/64 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

JSON has one number type, and it's a float

JSON numbers map onto IEEE 754 double-precision floats. There is no separate integer type in the spec; 5 and 5.0 are the same value. Most parsers preserve the integer/float distinction internally, but the spec doesn't require it.

Syntax rules

  • No leading zeros007 is invalid; write 7.
  • No + on positive numbers+5 is invalid; write 5.
  • Decimal point requires digits on both sides.5 and 5. are invalid; write 0.5 and 5.0.
  • Scientific notation OK1e6, 1.5e-3, 2E10 all valid.
  • NaN and Infinity are NOT allowed — encode as null, or as a tagged string by convention.

The 253 precision cliff

JS numbers (and most JSON parsers) lose integer precision above Number.MAX_SAFE_INTEGER (= 253 - 1 = 9,007,199,254,740,991). A 64-bit Twitter ID, a Stripe object ID, or a database BIGINT can silently round if you let it pass through a JS or default JSON parser. Send big integers as strings.

The Twitter ID bug: Twitter's API returns 64-bit tweet IDs. For a decade, naïve JS clients silently corrupted IDs above 253. Twitter's docs eventually told everyone to read id_str instead of id. APIs that send big integers as JSON numbers are landmines — assume strings unless documented otherwise.

Code

Valid numbers·json
{
  "int": 42,
  "negative": -17,
  "zero": 0,
  "float": 3.14159,
  "scientific": 6.022e23,
  "small": 1.5e-9,
  "big_id_as_string": "1234567890123456789"
}
Invalid (each line is a parse error)·text
007        ← leading zero
+5         ← explicit positive sign
.5         ← no leading digit
5.         ← no trailing digit
NaN        ← unquoted, not a value
Infinity   ← unquoted, not a value
Big integers in Python (works, JS rounds)·python
import json

# Python int has arbitrary precision
big = 9007199254740993  # 2**53 + 1
text = json.dumps({"id": big})
print(text)            # {"id": 9007199254740993}
back = json.loads(text)
print(back["id"] == big)  # True — round-trips fine

# Now feed that text through a JS engine:
# JSON.parse('{"id": 9007199254740993}').id
# → 9007199254740992  (silently rounded)

External links

Exercise

Write a JSON document with: a normal int, a negative float, a value in scientific notation, and a 64-bit ID encoded as a string. Then write the same data with the ID as a number. Parse both with JSON.parse (in node REPL) and compare the ID values. Watch the precision die in real time.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.