0.1 加 0.2,为什么不等于 0.3?一次讲清"钱到底该怎么存"
一、先看一个真实场景
小满开了家小店。这天做满减,一笔单子要按几个人的份额分摊优惠。
她用收银软件算了一遍,又拿纸笔手算了一遍。两个数,差了一分钱。
她揉了揉眼,再算。还是差一分。
客户没说什么,就是看她的眼神,像在问:"你是不是多收了我一分?"
钱她可以自己贴。可账对不上,人家凭什么信她?
她只想弄清一件很小的事:这一分,是从哪冒出来的?
二、这不是"算错了",也不是"显示问题"
很多软件算 0.1 加 0.2,结果不是 0.3。
是 0.30000000000000004。
第一反应通常是:"这就是显示的小毛病吧?"
不是。那一点差,是真的。
电脑存小数,用的是一种"差不多"的方式,叫浮点数。
打个比方:你写三分之一,写成小数是 0.3333……永远写不完。
二进制里的 0.1,也是这么个写不完的数。
写不完,就存不下。存不下,就只能存个近似值。
你存进去的 0.1,已经短了一丁点。两个这样的数加起来,就差那么一点。
三、三种存法,摆一排
| 存法 | 0.1 能精确表示吗 | 舍入可控 | 代价 | 适合 |
|---|---|---|---|---|
| 浮点数(float/double) | ❌ 不能 | ❌ 不可控 | 省空间 | 图形、科学计算 |
| 整数"分" | ✅ 能 | ✅ 固定两位 | 要统一最小单位 | 单一币种、支付 |
| 定点十进制(DECIMAL) | ✅ 能 | ✅ 可显式声明 | 稍慢、占一点空间 | 账务的默认选择 |
一句话:能"差不多"的地方,可以用浮点;要"一分不差"的地方,不行。
四、这段历史,值得知道
| 时间 | 事件 |
|---|---|
| 1985 | IEEE 754 发布,浮点算术有了国际标准 |
| 1982–1983 | 温哥华一家交易所的指数,每次只留三位小数、多出来的直接砍掉 |
| 22 个月后 | 指数从 1000 "跌"到 524.811,报纸说市场完了 |
| 1983-11 | 周末修复,指数一天跳回 1098.892 |
| 之后 | 金融账务普遍改用定点十进制 |
注意这段历史最反直觉的地方:指数腰斩,不是浮点的错,是"零头怎么处理"写错了。
五、判据只有一条:钱不是数字,是"账"
同样的误差,装在不同东西上,后果完全不同。
装在草稿、图片上:差 0.00000000000004,没人看得出来
>
装在钱的账本上:差 0.01,账对不上,得有人花一整天去找
草稿可以"差不多"。钱不行。
因为钱要能对账:你这一笔、我这一笔,得严丝合缝对上。
所以钱不是普通数字。钱是"账"。
六、真正出事的,常常不是"存不下"
上面那个交易所,问题不在浮点。
它的规则是:每次算完,小数点后只留三位,多的直接砍掉——不是四舍五入,是砍。
一天算两三千次。每次都往下砍一点点。
误差只朝一个方向攒。攒了 22 个月,指数就"腰斩"了。
这就是舍入规则:小数点后留几位、零头怎么进,必须先写清楚。
| 规则 | 做法 | 偏差方向 |
|---|---|---|
| 砍掉(截断) | 直接丢弃多余位 | 系统性向下,越攒越偏 |
| 四舍五入 | ≥5 进位 | 近似无偏,但 .5 恒向上 |
| 银行家舍入 | .5 向偶数进位 | 接近无偏,财务常用 |
七、那到底该怎么存
| 场景 | 做法 |
|---|---|
| 金额字段 | 用定点十进制,按"元角分"一位一位存 |
| 位数 | 别只留两位;多币种、税额会把需求撑大 |
| 汇率 | 别当固定数字;它是带编号、会过期的报价单 |
| 账本 | 只往上加,不改旧记录;能逐条验 |
| 舍入 | 规则写死在代码里,不靠默认 |
一个常见做法是:金额用 DECIMAL(20,4),税额用更高精度,币种用 ISO 4217 的三字母代码。
传输和展示时,金额当字符串传,别当数字——每转一道手,它就可能再被"差不多"一次。
八、四个常见误解
| 误解 | 事实 |
|---|---|
| "0.1+0.2 只是显示问题" | 不是。二进制里根本没有精确的 0.1。 |
| "小数点后两位就够" | 不一定。币种一多、税一摊,两位很快见底。 |
| "精度事故都是算错了" | 常常是舍入规则定错了。 |
| "汇率就是一个数字" | 它是一张会过期的报价单。 |
九、一句话带走
数字,可以"差不多"。钱不行:差一分,账就是错的。
下次你看见账单上差了一分钱,可以多问一句:是它算错了,还是它压根没把"钱"当"账"来存?
我们做身份与访问管理平台 Autional 时也踩过这条路:金额字段从浮点迁到定点,从两位小数迁到四位,一共改了 7 个模型。官网:https://www.autional.com
出处(外部资料)
- IEEE 754-2019《IEEE Standard for Floating-Point Arithmetic》。
- ISO 4217《Codes for the representation of currencies》。
- Wall Street Journal(1983-11-08)、Toronto Star(1983-11-29),温哥华交易所指数截断误差报道。
- McCullough & Vinod, Journal of Economic Literature(1999),对该事件的复盘。
浙公网安备 33010602011771号