1.浮点数精度问题

167 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

1.浮点数精度问题

java保留两位小数问题

方式1

四舍五入

 double f = 1122.33;
 BigDecimal b = new BigDecimal(f);
 double f1 = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();

方式2

 double d = 3.1415926;
 String result = String.format("%.2f");

(1)浮点数精确计算

胜利油田三流合一项目中一直存在一个问题,就是每次报表统计的物资金额和实际的金额要差那么几分钱,和实际金额不一致,让客户觉得总是不那么舒服,原因是因为我们使用java的浮点类型double来定义物资金额,并且在报表统计中我们经常要进行一些运算,但Java中浮点数(doublefloat)的计算是非精确计算,请看下面一个例子:

System.out.println(0.05 + 0.01);

System.out.println(1.0 - 0.42);

System.out.println(4.015 * 100);

System.out.println(123.3 / 100);

你的期望输出是什么?可实际的输出确实这样的:

0.060000000000000005

0.5800000000000001

401.49999999999994

1.2329999999999999

这个问题就非常严重了,如果你有123.3元要购买商品,而计算机却认为你只有123.29999999999999元,钱不够,计算机拒绝交易。

(2)四舍五入

是否可以四舍五入呢?当然可以,习惯上我们本能就会这样考虑,但四舍五入意味着误差,商业运算中可能意味着错误,同时Java中也没有提供保留指定位数的四舍五入方法,只提供了一个Math.round(double d)Math.round(float f) 的方法,分别返回长整型和整型值。round方法不能设置保留几位小数,我们只能象这样(保留两位):

public double round(double value){

return Math.round( value * 100 ) / 100.0;

}

但非常不幸的是,上面的代码并不能正常工作,给这个方法传入4.015它将返回4.01而不是4.02,如我们在上面看到的

4.015 * 100 = 401.49999999999994

因此如果我们要做到精确的四舍五入,这种方法不能满足我们的要求。

还有一种方式是使用java.text.DecimalFormat,但也存在问题,format采用的舍入模式是ROUND_HALF_DOWN(舍入模式在下面有介绍),比如说4.025保留两位小数会是4.02,因为 .025距离 nearest neighbor*”* ( .02.03)长度是相等,向下舍入就是 .02,如果是4.0251那么保留两位小数就是4.03

System.out.println(new java.text.DecimalFormat("0.00").format(4.025));

System.out.println(new java.text.DecimalFormat("0.00").format(4.0251));

输出是

4.02

4.03

(3)浮点数输出(科学记数法)

Java浮点型数值在大于9999999.0就自动转化为科学记数法来表示,我们看下面的例子:

System.out.println(999999999.04);

System.out.println(99999999.04);

System.out.println(10000000.01);

System.out.println(9999999.04);

输出的结果如下:

9.9999999904E8

9.999999904E7

1.000000001E7

9999999.04

但有时我们可能不需要科学记数法的表示方法,需要转换为字符串,还不能直接用toString() 等方法转换,很烦琐。