小白一枚,请多指教
小白今日任务:解决精度损失BUG
1、Mysql中的精度损失
1)数据准备
CREATE TABLE `demo` ( `f1` float(9,2) DEFAULT NULL, `f2` float(9,2) DEFAULT NULL, `d1` decimal(9,2) DEFAULT NULL, `d2` decimal(9,2) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into demo(f1,f2,d1,d2) values(1.31,1.21,1.31,1.21);
2)问题出现与解决
select truncate(f1-f2,2) as sub_f,truncate(d1-d2,2) as sub_d from demo; +-------+-------+ | sub_f | sub_d | +-------+-------+ | 0.09 | 0.1 | +-------+-------+
用truncate保留一位小数时,float出现精度损失,float与double都属于浮点数,在比较或者插入时可能会精度损失,double取值范围比较大,可能会造成精度不会损失的假象,所以正式环境涉及到精度比较高的场景,尽量用decimal类型表示
2、Java中的精度损失
@Test public void accuracyDemo() { // float float f = 1.231f; System.out.println("float is " + f * 100); // 原生double类型 double d = 1.231d; System.out.println("double is " + d * 100); // 用double直接转换 BigDecimal BigDecimal d2BigDecimal =new BigDecimal(d); System.out.println("double format BigDecimal is " + d2BigDecimal); // 用String直接转换 BigDecimal BigDecimal b = new BigDecimal("1.231"); System.out.println("String format BigDecimal is " + b.multiply(new BigDecimal("100"))); }
打印结果:
float is 123.09999 double is 123.10000000000001 double format BigDecimal is 1.2310000000000000941469124882132746279239654541015625 String format BigDecimal is 123.100
从结果可以看到float与double都出现了精度损失,因为double本身精度损失,所以转换后的BigDecimal也同样出现了精度损失,最后推荐使用用字符串转换BigDecimal,可以得到比较准确的结果
附:Java BigDecimal常用保留小数策略
1)直接舍去不需要的位数
BigDecimal keepPoint = new BigDecimal("1.236"); System.out.println(keepPoint.setScale(2,BigDecimal.ROUND_DOWN)); ----- 1.23
2)四舍五入
BigDecimal keepPoint = new BigDecimal("1.236"); System.out.println(keepPoint.setScale(2,BigDecimal.ROUND_HALF_UP)); ----- 1.24