MongoDB在4.2版本之后对聚合管道做了大量增强,其中$round操作符的加入让数值处理变得方便许多。在此之前,开发者只能通过$divide配合一些取巧的数学运算来模拟四舍五入,写法繁琐且容易出错。有了$round之后,保留两位小数、整数取整这类需求都可以在聚合阶段直接完成。本文将从语法、示例、对比和常见坑几个方面,详细介绍$round的用法。

$round操作符基础语法与参数含义
$round只能用在聚合管道阶段,常见的是$project、$addFields、$set、$group等阶段。它的基本语法有两种形式:单参数形式和双参数形式。单参数形式{$round: <number>}会将数值四舍五入到整数;双参数形式{$round: [<number>, <place>]}则可以指定保留的小数位数。
第二个参数place的含义需要特别注意:当place为正数时,表示保留对应位数的小数,比如2表示保留两位小数;当place为0或者省略时,表示四舍五入到整数;当place为负数时,则表示向小数点左侧移动,例如-1表示四舍五入到十位,-2表示四舍五入到百位。举个直观的例子,数值1736配合place为-2,结果就是1700。
第一个参数除了直接写数字字面量,更常见的用法是传入字段路径$字段名,或者传入一个表达式,比如先做$multiply运算再四舍五入。需要注意如果传入的字段值是null或者字段不存在,$round返回null;如果传入的是非数值类型,会直接报错。所以在处理脏数据时,建议先用$type判断字段类型,或者用$ifNull给默认值。
$round常见使用场景与聚合示例
先准备一份测试数据,假设有一个orders集合,存储了订单金额和商品评分:
db.orders.insertMany([
{ _id: 1, amount: 128.675, score: 4.623 },
{ _id: 2, amount: 99.333, score: 3.871 },
{ _id: 3, amount: 45.5, score: 4.5 }
])
第一个场景是对单个字段保留两位小数。比如统计报表中金额通常显示为两位小数,可以在$project阶段这样写:
db.orders.aggregate([
{
$project: {
amount: 1,
amountRounded: { $round: ["$amount", 2] }
}
}
])
// 输出结果中 _id:1 的 amountRounded 为 128.68
第二个场景是取整。比如计算订单数量、人数等指标时不需要小数,直接省略第二个参数即可:
db.orders.aggregate([
{
$addFields: {
amountInt: { $round: "$amount" }
}
}
])
// 128.675 四舍五入为 129,45.5 四舍五入为 46
第三个场景是配合$group做分组统计后取整。例如按店铺分组求平均客单价,并保留整数:
db.orders.aggregate([
{
$group: {
_id: "$shopId",
avgAmount: { $avg: "$amount" }
}
},
{
$project: {
shopId: "$_id",
avgAmount: { $round: ["$avgAmount", 0] },
_id: 0
}
}
])
这里建议先$group再$round,而不是在$group内部嵌套,因为$avg等累加器内部虽然支持表达式,但把取整逻辑放在单独的阶段里,管道结构更清晰,排查问题也更容易。
$round与$trunc、$floor、$ceil的区别
MongoDB中和数值处理相关的操作符还有好几个,很容易混淆。$trunc是截断,也就是直接丢弃指定位数之后的数字,不做任何进位。同样是128.675保留两位小数,$round得到128.68,而$trunc得到128.67。两者的语法完全一致,区别只在处理方式上。金融场景中如果业务规则要求舍位而不是四舍五入,就必须用$trunc。
$floor和$ceil则不支持指定小数位,只能作用于整数级别。$floor向下取整,128.675结果是128;$ceil向上取整,结果是129。此外还有一个$toInt可以直接截断小数部分转为整数,效果类似$floor只对正数成立,遇到负数时行为不同,比如-45.5经过$toInt是-45,而$floor是-46。
为了方便对比,整理成表格如下:
| 操作符 | 功能 | 支持指定小数位 | 示例 128.675 |
|---|---|---|---|
| $round | 四舍五入 | 是 | 128.68 |
| $trunc | 截断 | 是 | 128.67 |
| $floor | 向下取整 | 否 | 128 |
| $ceil | 向上取整 | 否 | 129 |
浮点数精度问题与注意事项
使用$round时最常踩的坑是浮点数精度。由于double类型的二进制存储特性,某些十进制小数无法精确表示。比如$round对2.675保留两位小数,结果可能是2.67而不是期望的2.68,因为2.675在内部实际存储为2.67499999...。这个问题不是MongoDB独有的,所有基于IEEE 754双精度浮点数的系统都有类似表现。
如果业务对精度要求严格,比如金额计算,推荐的方案是使用Decimal128类型(MongoDB中的NumberDecimal)。Decimal128是十进制浮点数,能精确表示0.1这类十进制小数,配合$round使用时结果符合十进制的直觉。插入数据时可以这样写:
db.orders.insertOne({
_id: 10,
amount: NumberDecimal("128.675")
})
db.orders.aggregate([
{ $match: { _id: 10 } },
{ $project: { amountRounded: { $round: ["$amount", 2] } } }
])
// 结果为 128.68,符合十进制四舍五入预期
另一个注意点是$round采用的是"四舍五入,逢五取偶数以外的数进位"的传统规则(round half up方向),即正好处于中间值时向远离零的方向进位,例如4.5取整得到5,-4.5取整得到-5。如果业务要求银行家舍入(逢五取偶),$round并不直接支持,需要在应用层自行处理或者用组合表达式实现。
最后提醒一点,$round只改变展示值,不会修改集合中的原始数据。如果希望把取整结果写回文档,需要配合$updateWithAggregationPipeline(MongoDB 4.2以上支持),用聚合表达式更新文档字段,这样才能让四舍五入的结果真正持久化到数据库中。
总结一下,$round是聚合管道中处理数值取整的首选操作符,掌握它的place参数正负含义、与$trunc的区别以及Decimal128的精度方案,基本可以覆盖绝大多数数值格式化场景。
MongoDB聚合管道$round四舍五入Mongodb取整修改时间:2026-09-14 13:12:54