math.round 实现“向远离零方向舍入”,返回 float64;正负数均对半进一,如 math.round(2.5)→3.0、math.round(-2.5)→-3.0;需显式转 int 且防溢出。

math.Round 在 Go 1.10+ 中可用,但它不等于“传统四舍五入”,也不返回整数类型——这是绝大多数人第一次用就出错的根本原因。
math.Round 是什么行为?
math.Round 实现的是 “round half away from zero”(向远离零方向舍入),不是银行家舍入(round to even),也不是 floor(x + 0.5) 那种简单逻辑:
-
math.Round(2.5)→3.0(向上) -
math.Round(-2.5)→-3.0(向下,即更远离 0) -
math.Round(0.5)→1.0,math.Round(-0.5)→-1.0
它返回 float64,不是 int。想转成整数必须显式转换:int(math.Round(x)),但要注意溢出风险(比如 x = 1e20)。
为什么 math.Floor(x + 0.5) 不可靠?
这个写法对正数基本可用,但对负数会崩:
-
x = -1.6→math.Floor(-1.6 + 0.5) = math.Floor(-1.1) = -2✅(-1.6 四舍五入应为 -2) -
x = -1.4→math.Floor(-1.4 + 0.5) = math.Floor(-0.9) = -1✅ -
x = -0.5→math.Floor(0.0) = 0❌(按 round half away from zero 应得-1.0)
真正安全的替代是:int(x + math.Copysign(0.5, x)),它能正确处理符号和边界。
如何四舍五入到 N 位小数?
最常用、性能好、无字符串开销的做法是放大 + math.Round + 缩小:
func RoundTo(x float64, n int) float64 {
p := math.Pow10(n)
return math.Round(x*p) / p
}
例如保留 2 位小数:RoundTo(3.14159, 2) → 3.14;RoundTo(2.65, 1) → 2.7(但注意:2.65 在 float64 中实际存的是略小的值,可能仍得 2.6 —— 这是 IEEE 754 本身限制,不是函数问题)。
别用 fmt.Sprintf + strconv.ParseFloat 做数值计算,它有分配开销、字符串解析误差,且无法保证中间值精度。
什么时候绝对不能用 float64 舍入?
涉及钱、计费、百分比配置、ID 映射等任何要求“十进制精确”的场景,float64 的舍入结果不可信:
-
1.021 * 1000可能算出1020.9999999999999,int()截断得1020,math.Round才得1021—— 但前提是原始1.021没在赋值时就被近似掉 - 正确做法:用
"github.com/shopspring/decimal",从字符串初始化:decimal.RequireFromString("1.021").Mul(decimal.NewFromInt(1000)) - 或直接用整数单位(如“分”代替“元”),全程
int64运算
浮点舍入的坑不在函数选错,而在于你是否意识到:你正在对一个本就不等于 2.65 的数做“四舍五入”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











