decimal不会丢精度而float会,因decimal以字符串方式定点存储,float按ieee 754二进制浮点存储导致0.1等无法精确表示;务必显式声明decimal(p,s)且s≥2。

因为FLOAT会丢精度,DECIMAL不会——这不是配置问题,是底层存储机制决定的。
DECIMAL 用字符串存数字,FLOAT 用二进制近似存
DECIMAL 在 MySQL 内部以定点数方式处理,实际把数值当作带小数点的字符串解析和运算(比如 '123.45'),整个过程不经过二进制转换。FLOAT 则严格遵循 IEEE 754 标准,把十进制小数转成二进制浮点表示,而像 0.1、0.01 这类十进制小数在二进制里是无限循环小数,必然截断或舍入。
常见错误现象:
- 插入
12.34到FLOAT(10,2)字段,查出来变成12.34000015或12.33999999 -
SUM()聚合后差几分钱,尤其在多行累加或跨表 JOIN 后更明显 - WHERE 条件写
amount = 99.99查不到数据,因为实际存的是99.9899999
DECIMAL(p,s) 的 p 和 s 必须显式指定,不能依赖默认值
MySQL 对 DECIMAL 的默认行为是 DECIMAL(10,0),也就是“整数型小数”,小数位为 0。如果你没写明 s,比如建表时只写 DECIMAL(10),那所有金额都会被截断成整数,99.99 存进去就是 99。
实操建议:
- 国内通用场景用
DECIMAL(13,2):整数部分最多 11 位(支持千亿级交易额),小数固定 2 位 - 需要分币单位(如日元)或手续费精度更高时,用
DECIMAL(15,4)或DECIMAL(18,6) - 避免用
DECIMAL(10,2)存大额流水——超限会报错或四舍五入,不是静默截断
FLOAT 类型的 (M,D) 是非标准语法,且行为不可靠
MySQL 允许写 FLOAT(10,2),但这只是显示宽度提示,**不影响实际存储精度**。它依然按单精度浮点规则存,M 和 D 仅控制显示格式(比如补零),对底层二进制表示毫无约束。
典型陷阱:
-
FLOAT(5,2)看似只能存99.99,但插入123.45不报错,而是存成近似值123.45000457763672 - 不同 MySQL 版本、不同硬件平台下,同样的 FLOAT 计算结果可能略有差异
- ALTER TABLE 修改 FLOAT 列精度时,原有数据可能被重写并引入新误差
别信“只要不参与计算就没事”这种说法
哪怕你只做展示、不做 SUM/AVG,只要涉及比较、排序、索引查找,FLOAT 就可能出问题:
-
ORDER BY amount DESC可能把100.00排在99.99后面,因为实际值是99.990001和100.000001 - 联合索引中如果包含 FLOAT 字段,范围查询(
BETWEEN)边界可能漏数据 - 应用层读取后做 JSON 序列化,JavaScript 的 Number 类型会进一步放大误差
真正安全的做法只有一条:所有跟钱有关的字段,类型必须是显式声明的 DECIMAL(p,s),且 s ≥ 2。其他任何妥协,长期来看都会在某个凌晨三点的对账单里露出破绽。











