MySQL 设置double列累加,导致精确缺失

最近做工厂项目,测试提的一个bug在本地测了好久一直没复现;直接连测试线的数据库,又经过一系列流程模拟最终在本地复现了这个问题;由于有消息日志的定时输出,只能打点介入来追踪bug,最后发现问题出在对double列累加的SQL语句上,真是一顿好找。

百度‘mysql 设置double列累加’,发现精度缺失对于double类型是不可控的。网上都建议将表字段更换为decimal类型,对于上线已久或开发完成的项目而言,显然是不合适的,因为改了列的类型又会引发列取值问题。

由此,我根据本地已复现的bug做了如下测试;

新建一张表:

CREATE TABLE `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `numDouble` double NOT NULL COMMENT 'double类型取值',
  `numDoubleLimit` double DEFAULT NULL COMMENT 'double类型精确取值',
  `numDecimal` decimal(10,5) NOT NULL COMMENT 'decimal精确取值',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

先插入一条数据:

INSERT INTO `test` (`id`, `numDouble`, `numDoubleLimit`, `numDecimal`) VALUES ('1', '0', '0', '0.00000');

执行以下SQL三次:

# double列累加
UPDATE test SET numDouble = numDouble + 192.2 WHERE id = 1;
# double列取约数后累加,double类型会在0后的剩余长度补足一些小数,而大概就是这些小数导致的double类型求和后精度缺失 UPDATE test SET numDoubleLimit
= ROUND(numDoubleLimit + 192.2, 5) WHERE id = 1;
# decimal列累加 UPDATE test SET numDecimal
= numDecimal + 192.2 WHERE id = 1; SELECT numDouble,numDoubleLimit,numDecimal FROM test;

发现结果如下:

 解决方案:

double列取约数后再累加求和,问题解决!

由此,总结如下:

1、追查日志,在大项目中由于定时器任务等复杂业务,会导致打印大量的日志,如果没有关键日志就会很难追踪bug;
2、开发完的项目在测试线总会出现一些奇奇怪怪的问题,最好可以直接连测试环境直接复现问题,然后再逐一排除问题;
3、找到问题的前后逻辑,并联系'上下文',有利于确认问题位置、确定问题处理方案;
4、设计表的小数字段类型时,形如金额、收货数量等要求精度的属性时可以用decimal;如果担心decimal类型的列取值问题,也可以使用double类型,但是最好不要用SQL直接累加double列,建议在外层代码计算好double列值,SQL语句仅set保存double列的值

*代码中的double列求和精度缺失,请参考:

复制代码
double a = 192.2D;
double b = 0D;
b += a;
b += a;
b += a;
System.out.println(b);// 结果:576.5999999999999
        
BigDecimal aDecimal = new BigDecimal(Double.toString(192.2D));
BigDecimal bDecimal = new BigDecimal(Double.toString(0D));
bDecimal = bDecimal.add(aDecimal).add(aDecimal).add(aDecimal);
System.out.println(bDecimal.doubleValue());// 结果:576.6
复制代码

 补充示例:

复制代码
double a = 0.21;
double b = 0.0;
double c = 0.11;
        
double d = a - b - c;
System.out.println(d);// 0.09999999999999999
        
double e = 0.21 - 0 - 0.11;
System.out.println(e);// 0.09999999999999999
复制代码

 

posted @   王晓鸣  阅读(1487)  评论(1编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示