DECIMAL 和 NUMERIC 能避免浮点数精度丢失,因为它们是定点数类型,以整数形式存储“整数部分+小数位数”,不依赖 IEEE 754 浮点表示,全程无舍入误差。

为什么 DECIMAL 和 NUMERIC 能避免浮点数精度丢失
因为它们是定点数类型,底层以整数形式存储「整数部分 + 小数位数」,不依赖二进制浮点表示。比如 DECIMAL(10,2) 表示最多 10 位数字、其中小数占 2 位,值 1234567.89 会被精确存为整数 123456789 再除以 100,全程无舍入误差。
而 FLOAT 或 REAL 类型按 IEEE 754 存储,像 0.1 + 0.2 在数据库里可能算出 0.30000000000000004 —— 这在金融、计费、库存扣减等场景不可接受。
DECIMAL 声明时的两个参数怎么选
声明形如 DECIMAL(M,D),M 是总位数(精度),D 是小数位数(标度)。选错会导致截断或报错:
-
M必须 ≥D,否则建表失败(如DECIMAL(2,5)不合法) - 插入超长整数(如向
DECIMAL(5,2)插入1234.567)会四舍五入成1234.57;但插入12345.67会直接报错,因整数部分已超5−2=3位 - 金融常用
DECIMAL(19,4)(覆盖万亿级金额+四位小数,适配大多数会计系统);积分、优惠券面额可用DECIMAL(12,2)
从 FLOAT 迁移数据到 DECIMAL 的坑
直接 ALTER TABLE ... MODIFY COLUMN 可能静默截断或四舍五入,尤其当原字段含大量非精确值(如 0.1 实际存为 0.10000000149011612):
- 先用
SELECT column_name, CAST(column_name AS DECIMAL(12,2)) FROM table预览转换效果 - 避免用
ROUND()二次处理,它可能掩盖原始误差;应优先重算源头数据 - MySQL 8.0+ 支持
ALTER TABLE ... ALTER COLUMN ... SET DEFAULT,但默认值也需显式写成字符串或DECIMAL字面量(如DEFAULT 0.00不安全,改用DEFAULT '0.00')
应用层读写 DECIMAL 时的常见疏漏
ORM 或驱动常把 DECIMAL 当作字符串或浮点数返回,导致二次精度丢失:
- Python 的
pymysql默认将DECIMAL返回为decimal.Decimal,但若配置了cursorclass=pymysql.cursors.DictCursor且未启用decimal解析,可能退化为float - Java JDBC 中需确认
ResultSet.getBigDecimal()被调用,而非getDouble();Spring Boot 的@Column(precision = 19, scale = 4)仅影响 DDL,不强制运行时类型 - JSON 序列化时,
decimal.Decimal默认无法直出,需自定义 encoder 转为字符串,否则经float中转就废了
真正关键的不是“换类型”,而是确保从存储、传输到展示,整条链路都拒绝浮点数介入。哪怕数据库用了 DECIMAL,只要中间某一层用了 parseFloat() 或 float64 解析,精度就归零了。










