float64加减法永远算不准是因二进制无法精确表示十进制小数,如0.1实际存储为0.10000000000000000555…,导致舍入误差不可逆;比较需用math.Abs(a-b)
Go语言原生
float64加减法无法真正“控制精度”,因为误差在赋值瞬间就已固化;必须换数据类型——金融用shopspring/decimal,科学计算用math/big的big.Float或big.Rat,整数运算用big.Int。为什么
float64加减法永远算不准?不是代码写错,是二进制表示的硬限制:
0.1在float64里实际存的是0.10000000000000000555...,所有后续加减都在这个失真值上进行。哪怕只做一次0.1 + 0.2,结果0.30000000000000004也已是不可逆的舍入误差。
fmt.Printf("%.2f", 2.555)输出2.55?因为2.555 * 100在底层变成255.49999999999997,math.Round向下取整- 用
==比较两个float64加减结果?几乎必然失败,必须改用math.Abs(a-b)- 把
float64塞进big.Float再算?白搭——SetFloat64(0.1)传进去的已经是错的
shopspring/decimal:金融场景唯一靠谱选择它不模拟浮点,而是用整数+小数位数(scale)精确表示十进制数,加减乘除全保精度。
- 初始化必须从字符串或整数比例来:
decimal.NewFromFloat(28860.000000001)会带入原始误差;正确写法是decimal.RequireFromString("28860.000000001")- 加减法直接调
.Add()/.Sub(),返回新decimal.Decimal,不修改原值- 除法要指定精度和舍入模式:
a.Div(b, 10).Round(2)(保留10位中间精度,最终四舍五入到2位)- 别用
float64()方法转回原生类型——那又掉回误差坑里
math/big系列:需要手动对齐精度的“高精度裸金属”
big.Int适合超大整数(如密码学、ID生成),big.Float和big.Rat才管小数,但每一步都得你亲手把关。
big.Float精度单位是二进制位(bit),不是小数位:要100位十进制精度,得设SetPrec(333)(≈ log₂(10¹⁰⁰)),默认64位≈19位十进制,跟float64差不多- 所有输入必须用
SetString("3.1415926535"),绝不用SetFloat64(3.1415926535)- 两个
big.Float相加前,确保它们prec一致,否则低精度操作数会污染结果big.Rat适合有理数运算(如分数加减),内部存分子分母,无舍入,但无法表示√2这类无理数最容易被忽略的坑:字符串转换不是万能解药
有人用
fmt.Sprintf("%.2f", x)再strconv.ParseFloat假装“修复精度”,这其实只是把误差截断成固定位数的错值,且性能差、易出错。
fmt.Sprintf的%.2f是四舍五入,不是截断;要截断得自己写逻辑或用decimal的Truncate()- 字符串解析时若含空格、逗号、千分位符号,
ParseFloat直接失败,而decimal.RequireFromString会报明确错误- 高频循环里反复字符串转换,GC压力陡增,
decimal或big.Float复用实例才能扛住
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!












