You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mongoose v5.11.11数值增量精度异常问题咨询(Node.js v12.18.3环境)

Mongoose $inc 操作导致浮点数精度异常的原因与解决方法

这个问题我之前做金融类项目时也踩过坑,本质是二进制浮点数的精度限制在搞鬼,再加上Mongoose的setter逻辑和MongoDB原子操作的特性冲突,才出现了这种不符合预期的结果。咱们一步步拆解原因,再给可行的解决方案。

原因分析

JavaScript里的Number类型是基于IEEE 754标准的双精度浮点数,这种存储方式无法精确表示所有十进制小数——比如0.29和8.01在二进制里都是无限循环的近似值。

而你的代码里存在两个关键问题:

  1. Schema中的set方法只在初始赋值、直接修改字段值时生效,$inc是MongoDB的原子更新操作,会直接绕过Mongoose的setter逻辑,直接对数据库里存储的二进制浮点数做运算;
  2. 两个不精确的浮点数相加后,近似值的误差被放大,最终就出现了8.299999999999999这种看起来“奇怪”的结果。

解决方法

这里有三个靠谱的方案,你可以根据业务场景选择:

1. 改用整数存储(最推荐)

这是金融类系统的通用做法:把金额类数值放大100倍,以“分”为单位存储整数,彻底规避浮点数精度问题。修改代码如下:

// 更新后的Schema
const Schema = new Schema({ 
  total: { 
    type: Number, 
    default: 801, // 8.01元 → 801分
  }
})

// 控制器函数:传入的addValue需转成“分”单位
let myController = (id, addValue) => {
  const addInCents = Math.round(addValue * 100); // 0.29元 → 29分
  return model.findByIdAndUpdate(id, { $inc: { total: addInCents } })
}

读取数据时再除以100转成“元”即可,完全不会有精度丢失的问题。

2. 绕开原子操作,触发Mongoose的setter逻辑

如果不想修改存储结构,可以先查询文档,再通过修改字段值触发setter的四舍五入,最后保存:

let myController = async (id, addValue) => {
  const doc = await model.findById(id);
  doc.total += addValue; // 这里会触发set方法的Math.round(v*100)/100
  return doc.save();
}

⚠️ 注意:这种方式不是原子操作,如果有并发更新的场景,可能会出现数据覆盖问题,需要结合事务或者版本锁(比如versionKey)来处理。

3. 使用MongoDB的Decimal128类型

Mongoose支持MongoDB原生的Decimal128类型,它能精确表示十进制小数。修改代码如下:

const { Decimal128 } = require('mongoose').Types;

// 更新后的Schema
const Schema = new Schema({ 
  total: { 
    type: Decimal128, 
    default: Decimal128.fromString('8.01'),
    set: v => Decimal128.fromString((Math.round(Number(v)*100)/100).toString())
  }
})

// 控制器函数:传入的addValue需转成Decimal128类型
let myController = (id, addValue) => {
  const incValue = Decimal128.fromString(addValue.toString());
  return model.findByIdAndUpdate(id, { $inc: { total: incValue } })
}

这种方式能精确存储,但查询返回的是Decimal128对象,需要调用.toString()或转成数字后使用,适合对精度要求极高且无法修改整数存储的场景。

内容的提问来源于stack exchange,提问作者Sergey Ivanov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 12:18:11