bigywater.tech
MySQL / InnoDB · 79

DECIMAL 与浮点数

用FLOAT和DECIMAL分别存0.1和0.2,再把同样的误差放大到1000次累加,看金额字段为什么真实不该用浮点数。

0.30000000447034836
FLOAT存0.1+0.2真实的结果
10.00 vs 9.999999776482582
DECIMAL与FLOAT累加1000次的真实差距

做的事:同样的0.1和0.2,分别用FLOAT和DECIMAL存

代码CREATE TABLE money_float (amt FLOAT); INSERT INTO money_float VALUES (0.1), (0.2); CREATE TABLE money_decimal (amt DECIMAL(10,2)); INSERT INTO money_decimal VALUES (0.1), (0.2);

验证:FLOAT真实存不准0.1+0.2,DECIMAL真实精确

实测 · 本机执行输出
SELECT SUM(amt) FROM money_float;
→ 0.30000000447034836   ← 真实不是干净的0.3

SELECT (0.1的行) + (0.2的行) = 0.3 FROM money_float;
→ 0   (false,真实判断不相等)

SELECT SUM(amt) FROM money_decimal;
→ 0.30   ← 真实精确

SELECT (0.1的行) + (0.2的行) = 0.3 FROM money_decimal;
→ 1   (true,真实判断相等)

FLOAT底层用的是IEEE 754二进制浮点数,0.1和0.2这类十进制小数在二进制里真实是无限循环小数,只能被截断近似存储,加起来的结果真实带着这个近似误差;DECIMAL是按十进制精确存储的定点数(真实内部按数字串+位置来存,不经过二进制浮点转换),0.1+0.2在DECIMAL的世界里就是真实精确的0.3,不多不少。

验证:把误差放大到1000次累加,差距真实更明显

实测 · 本机执行输出(1000行,每行都是0.01)
FLOAT累加1000次:     SUM = 9.999999776482582   (期望10.00,真实差了约0.0000002)
DECIMAL(10,2)累加1000次: SUM = 10.00               (真实精确,一分不差)
单次的0.1+0.2误差看起来微不足道,但这类误差真实是会累积的——本文这次1000次0.01累加的测试里,FLOAT真实偏离了预期值,虽然绝对误差本身还很小(约0.0000002),但如果这是真实的金额字段,累积上百万笔交易之后,这个误差真实可能被放大到肉眼可见的程度,而且没有规律可循,没法简单"四舍五入"修正回来。这正是"金额字段永远用DECIMAL,不要用FLOAT/DOUBLE"这条建议真实的由来——DECIMAL牺牲了一些存储空间和计算性能(它不是CPU原生支持的浮点运算,要靠软件模拟十进制运算),换来的是真实精确、可预测、不会因为累加次数增多而漂移的结果,这个权衡对涉及金额的字段几乎总是值得的。

分步演示:从单次相加到1000次累加,误差的真实放大

每一步都是本文上面用真实的FLOAT和DECIMAL列跑出来的原始结果。

FLOAT的误差真实会累积,DECIMAL真实不会
这一步新验证的类型

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),FLOAT和DECIMAL(10,2)两张表,对比0.1+0.2和1000次0.01累加
    京ICP备2026067502号-1京公网安备11010502063487号