应优先使用 decimal 而非 float/double 进行精确计算,因后者受二进制浮点精度限制(如 0.1+0.2≠0.3),而 decimal 以十进制定点运算保证精度;需显式指定 decimal(m,d) 参数,避免默认 decimal(10,0) 截断小数;m 为总位数、d 为小数位数且 d≤m,超出范围会报错;decimal 存储层硬约束精度,适合等值判断与金融场景,但修改精度需全表重写并严格对账;初期设计须覆盖业务最大值与最小分辨率(如跨境结算需 decimal(12,4))。

浮点数计算结果不等于预期,比如 0.1 + 0.2 返回 0.30000000000000004
这是二进制浮点表示的固有缺陷:十进制小数 0.1 在二进制中是无限循环小数,存储时必须截断,导致精度丢失。MySQL 的 FLOAT 和 DOUBLE 都基于 IEEE 754 标准,无法避免这个问题。而 DECIMAL 内部以字符串或定点编码方式存储,全程按十进制运算,0.1 + 0.2 就严格等于 0.30(取决于定义的标度)。
DECIMAL(M,D) 必须显式声明精度,否则默认 DECIMAL(10,0) 会丢掉所有小数位
很多人建表时只写 DECIMAL,没带参数,结果插入 12.99 变成 13——因为默认 D=0,小数部分被直接截断。电商常用 DECIMAL(10,2)(最大 99999999.99),高精度计费场景可能用 DECIMAL(12,4)。注意:M 是总位数,D 是小数位数,且 D ≤ M;超出范围会报 Data truncated for column 'xxx' at row 1 警告。
插入和查询行为差异大:FLOAT 显示格式不约束实际值,DECIMAL 强制四舍五入+校验
FLOAT(7,4) 插入 999.00009 会变成 999.0001(四舍五入),但再查出来可能因显示精度又变样;而 DECIMAL(7,4) 同样插入 999.00009,也会四舍五入为 999.0001,但后续任何读取、计算都稳定复现该值。关键区别在于:DECIMAL 的精度是存储层硬约束,FLOAT 的精度只是近似表示,不可靠用于等值判断(如 WHERE amount = 19.99)。
上线后改 DECIMAL 精度要锁表,FLOAT 改精度却可能“悄无声息地出错”
ALTER TABLE t MODIFY COLUMN price DECIMAL(12,4) 会触发全表重写,耗时且阻塞写入;但把 FLOAT 改成 DOUBLE 或调 M,D 参数,表面快,实则掩盖了原有精度问题——旧数据仍按原浮点规则解释,新插入逻辑又不同,混在一起反而更难排查。金融类业务一旦上线,DECIMAL 的精度边界就是业务边界,改它不是技术动作,而是需要财务对账兜底的动作。
真正容易被忽略的不是“该不该用 DECIMAL”,而是 M 和 D 的组合是否覆盖了业务全生命周期的最大值和最小分辨率——比如跨境结算含 4 位小数,但订单表用了 DECIMAL(10,2),后期加字段或改类型成本远高于初期设计多想两秒。











