Numbers, Dates, and Decimal128

BSON has four numeric types where JavaScript has one, and the driver picks between them for you. An integral JavaScript number within the signed 32-bit range serializes as Int32 (type 16, 4 bytes); every other number — fractional, or beyond ±2^31 — becomes a double (type 1, 8 bytes), exact only to 2^53. Long (type 18, 8 bytes) is the explicit 64-bit integer; a one-field document holding an Int32 measures 12 bytes against 16 for a Double or a Long. Decimal128 (type 19, 16 bytes) is IEEE 754 decimal floating point with 34 significant digits and base-10 arithmetic — the type for money.

Four numeric types, and what each one keepsJavaScript
import { serialize, deserialize, Long, Decimal128 } from 'bson';
const back = (d) => deserialize(serialize(d));
const bsonType = (v) => serialize({ v })[4];        // the type byte of the only field
console.log(back({ count: 7, ratio: 7.5, big: Long.fromString('9007199254740993') }));
console.log('type bytes  :', [5, 5.5, 2 ** 31].map(bsonType), '(16 = int32, 1 = double)');
console.log('double math :', 0.1 + 0.2);
console.log('decimal doc :', back({ total: Decimal128.fromString('19.99') }));
console.log('date kept   :', back({ at: new Date('2026-09-22T08:30:00.123456Z') }).at);
Output
{ count: 7, ratio: 7.5, big: new Long('9007199254740993') }
type bytes  : [ 16, 1, 1 ] (16 = int32, 1 = double)
double math : 0.30000000000000004
decimal doc : { total: new Decimal128('19.99') }
date kept   : 2026-09-22T08:30:00.123Z

Two lessons sit in that output. 9007199254740993 survives as a Long but would have become 9007199254740992 as a double. And 0.1 + 0.2 is why Decimal128 round-trips 19.99 exactly where a double named price does not: store currency as Decimal128 or as an integer count of cents.

Type is part of identity, and JavaScript hides which one you chose. { qty: 5 } written from mongosh 403 holds an int32, and so does { qty: 5.0 }, because the two literals are the same number in JavaScript; only Double(5) forces a double. A query for { qty: 5 } matches all of them, but { qty: { $type: 'int' } } matches the first two. Keep a single type per field, and repair a mixed one with $toInt or $toDecimal (The Aggregation Pipeline).

BSON Date (type 9) is a signed 64-bit count of milliseconds since the epoch, UTC, with no time zone and no sub-millisecond digits — the .123456 above was truncated to .123. The unrelated BSON Timestamp (type 17) is two 32-bit halves reserved for the oplog.