Numbers: Int32, Int64, Double and Decimal128
Add three prices of 0.10 in plain JavaScript and you get 0.30000000000000004. For a price on a book page that is harmless; on an invoice it is a bug. BSON offers four kinds of number so you can choose between speed, range and exactness.
The rule
Whole numbers are Int, rough decimals are Double, money is Decimal128.
| Type | $type name |
Holds | mongosh |
|---|---|---|---|
| Int32 | "int" |
whole numbers up to about ±2.1 billion | 12 or NumberInt("12") |
| Int64 | "long" |
whole numbers up to about ±9.2 quintillion | NumberLong("3000000000") |
| Double | "double" |
64-bit floating point, about 15 significant digits | 9.99 |
| Decimal128 | "decimal" |
34 exact decimal digits | NumberDecimal("9.99") |
In mongosh, a whole number that fits in 32 bits is saved as Int32, and a number with a fractional part is saved as a Double. So in the books collection, stock: 12 is an Int32 and price: 9.99 is a Double. Circe's price: 12 is an Int32, and that is fine: numeric types compare by value, so { price: 12 } matches Circe whatever type stores the 12.
A Double stores numbers in binary, so most decimal fractions, such as 0.1 or 9.99, are kept only approximately. A Decimal128 stores decimal digits exactly, which is why money belongs there. Pass it as a string, NumberDecimal("9.99"): a bare 9.99 would already be an approximate Double before the conversion.
The shortcut
Ask two questions in order: "Can it have a fraction?" then "Must it be exact, or can it be huge?"
Copies in stock
No fraction, never near 2 billion. Int32, the mongosh default for
7.
The trap: writing NumberLong(3000000000123456789) with a bare number. JavaScript turns it into a Double first and loses digits, so always quote big or exact values.
Drills
Answer fast — trust the rule.
In mongosh you insert { stock: 7 }. What BSON type is stock?
Which type should hold a price that is summed on invoices?
A counter will reach 5 billion. Which type fits it?
Circe's price is the Int32 12. Does db.books.find({ price: 12.0 }) find it?
Why write NumberDecimal("9.99") rather than NumberDecimal(9.99)?
Remember
- Whole numbers are Int, rough decimals are Double, money is Decimal128.
- Shortcut: fraction or not, then exact or huge; mongosh defaults to Int32 and Double.
- Trap: pass big or exact numbers as quoted strings to
NumberLongandNumberDecimal.
