对账时发现报表差了几分钱,往回查八成是金额字段用了 float。这就得请出 decimal 这种数据类型,专门治这个毛病。

一种精确的十进制类型

decimal 是精确小数数据类型,按十进制方式存数,没有二进制浮点那套舍入误差。数据库里最常见,比如 MySQL 的 DECIMAL(10,2),括号里 10 是总位数,2 是小数位,能存到亿级还保留两位小数,放金额正合适。存储空间按位数算,9 位十进制大约 4 个字节,精度高也不会大得离谱。定义了 DECIMAL 字段后,程序里的映射类型也要对应上,别一边精确一边又转成 double。

float 为什么会差几分钱

float 和 double 是二进制浮点数,0.1 这种数在二进制里是无限循环小数,存进去只能是近似值。单条数据看不出问题,几万条加加减减,误差就冒头了,0.1 加 0.2 不等于 0.3 就是这个原因。decimal 按十进制一位一位存,加出来就是准的。

不止数据库有

Python 里是 decimal 模块的 Decimal 类,from decimal import Decimal 之后,Decimal('0.1') 加 Decimal('0.2') 精确得到 0.3,需要更高精度就用 getcontext 调整。Java 里对应的叫 BigDecimal,套路一样。建表时还有种思路是金额全用整数存「分」,同样能避开误差,两种方案都常见。

什么时候用、什么时候别用

  • 金额、费率、折扣、积分这些碰钱的地方,必须 decimal
  • 重量、长度这类要精确计量的业务数值,也适合
  • 科学计算、统计、机器学习特征值,用 float 反而更快更省内存

注意小数位溢出的表现:往 DECIMAL(5,2) 里插 1234.567,严格模式下直接报错,宽松模式会四舍五入并给个警告,批量导入时这种静默截断最坑,导入完核对一遍总数。

一句话记住:跟钱沾边的字段,数据库里给 DECIMAL,代码里用对应的 Decimal 类,别拿 float 硬扛,事后对账能省一堆麻烦。