必须用字符串初始化decimal128,如new decimal128("123.45");混用数值类型会导致聚合时无声降级为double而丢失精度;$todecimal不支持字符串或布尔值输入,需前置校验;查询应优先用$isnumber粗筛可参与运算的数值字段。

必须用 Decimal128 构造器显式创建,不能依赖自动转换;聚合中混用类型会无声降级为 double,精度直接丢失。
插入时为什么 new Decimal128(123.45) 是错的
JavaScript 中 123.45 本身就是 double 字面量,传给 Decimal128 构造器前精度已丢失。比如 new Decimal128(0.1 + 0.2) 实际传入的是 0.30000000000000004,再包装也救不回来。
正确做法只有一条:始终用字符串初始化:
-
new Decimal128("123.45")✅ -
Decimal128.fromString("99999999999999999999999999999999.99")✅(支持 34 位) -
new Decimal128(123.45)❌(浮点字面量先失真) -
Decimal128.fromString(123.45)❌(参数非字符串,会转成 "123.450000000000000000000000000000" 再解析,不可控)
聚合里用 $sum 或 $avg 却返回 double?检查字段混合
$sum 对纯 Decimal128 字段天然保持类型,结果仍是 Decimal128;但只要参与运算的任意一个值是 int、long 或 double,整个结果就强制降级为 double —— 这个过程不报错、不警告,只默默丢精度。
常见踩坑点:
- 历史数据里混着
NumberInt(100)和新插入的Decimal128("100.00"),$sum结果变成200.0(double)而非Decimal128("200.00") - 用
$toDecimal转换字符串字段时,若源字段含空值或非法格式(如"N/A"),整个$toDecimal表达式会报错中断聚合,需前置用$cond过滤或兜底 -
$addFields中写{ total: { $sum: ["$price", "$tax"] } },哪怕两个字段都是Decimal128,若其中一个是null,$sum返回null,不是Decimal128
查询时判断字段是否为高精度小数:别只用 $type
$type: "decimal" 只能匹配严格为 Decimal128 的字段,但金融场景常需把 int、long、double 也视作“可参与金额运算的数值”,这时该用 $isNumber。
例如查所有能安全用于加减的 amount 字段:
{ $match: { $expr: { $isNumber: "$amount" } } }
它会同时命中:
-
Decimal128("123.45")→true -
NumberLong("123")→true -
NumberInt(123)→true -
"123.45"(字符串)→false -
null或缺失字段 →false
注意:$isNumber 不区分精度,只是“能当数字用”的粗筛;真要校验是否为 Decimal128,仍得用 $type: "decimal"。
$toDecimal 不是万能兜底,它对非数值输入直接失败
$toDecimal 设计目标是明确转换,不是容错处理。遇到 null、missing、string(如 "123.45")、bool 等,行为分三类:
-
null或缺失字段 → 返回null - 合法数字类型(
int/long/double)→ 正确转成Decimal128 - 字符串或布尔值 → 报错:
PlanExecutor error during aggregation :: caused by :: Conversion from string/boolean to decimal is not supported
所以实际使用必须包裹一层判断:
{ $cond: { if: { $and: [ { $ne: ["$rawAmount", null] }, { $isString: "$rawAmount" } ] }, then: { $toDecimal: "$rawAmount" }, else: "$rawAmount" } }
更稳妥的做法是:入库前清洗,别把字符串金额塞进数值字段。
Decimal128 的真正难点不在存储,而在整条链路——从 driver 初始化、到聚合阶段类型推导、再到应用层反序列化——任何一环用错构造方式或忽略隐式降级,精度就没了。它不像 int 那样“写了就准”,而像手术刀:锋利,但要求每一步都稳。











