Unpredictable double
本问题已经有最佳答案,请猛点这里访问。
Possible Duplicate:
Double precision problems on .NET
Double calculation producing odd result
我知道,双值
代码:
1 2 3 4 5 6 7 8 9 10 11 |
输出:
1 2 3 4 5 6 7 | 0.4 0.03 0.3 0.1 0.19999999999999998 2.9999999999999996 0.039999999999999994 |
实际上发生了什么?加法,乘法很好,但减法,除法不行。有人能解释一下为什么加法和减法不同吗?
如果你渴望精确,就用bigdecimal。
1 2 3 4 5 6 7 8 9 10 11 | public static void main(String[] args) { BigDecimal d = BigDecimal.valueOf(0.3d); BigDecimal f = BigDecimal.valueOf(0.1d); System.out.println(d.add(f)); System.out.println(d.multiply(f)); System.out.println(d); System.out.println(f); System.out.println(d.subtract(f)); System.out.println(d.divide(f)); System.out.println((d.subtract(f)).multiply(d.subtract(f))); } |
产量
1 2 3 4 5 6 7 | 0.4 0.03 0.3 0.1 0.2 3 0.04 |
或者舍入您的结果,decimalFormat可以很好地做到这一点,使用符号意味着仅在必要时显示小数。
1 2 3 4 5 6 7 8 9 10 | double d = 0.3d; double f = 0.1d; DecimalFormat format = new DecimalFormat("#.##"); System.out.println(format.format(d + f)); System.out.println(format.format(d * f)); System.out.println(format.format(d)); System.out.println(format.format(f)); System.out.println(format.format(d - f)); System.out.println(format.format(d / f)); System.out.println(format.format((d - f) * (d - f))); |
产量
1 2 3 4 5 6 7 | 0.4 0.03 0.3 0.1 0.2 3 0.04 |
简而言之,对于浮点运算,您有一个表示错误和一个舍入错误。
解决方案是要么使用bigdecimal,要么对结果取整。
如果您使用bigdecimal,它将显示您真正拥有的精确值。
1 2 3 4 5 6 7 8 9 10 11 12 | double d = 0.3d; double f = 0.1d; System.out.println("d=" + new BigDecimal(d)); System.out.println("f=" + new BigDecimal(f)); System.out.println("d+f=" + new BigDecimal(d + f)); System.out.println("0.4=" + new BigDecimal(0.4)); System.out.println("d*f=" + new BigDecimal(d * f)); System.out.println("0.03=" + new BigDecimal(0.03)); System.out.println("d-f=" + new BigDecimal(d - f)); System.out.println("0.2=" + new BigDecimal(0.2)); System.out.println("d/f=" + new BigDecimal(d / f)); System.out.println("(d-f)*(d-f)=" + new BigDecimal((d - f) * (d - f))); |
印刷品
1 2 3 4 5 6 7 8 9 10 | d= 0.299999999999999988897769753748434595763683319091796875 f= 0.1000000000000000055511151231257827021181583404541015625 d+f= 0.40000000000000002220446049250313080847263336181640625 0.4= 0.40000000000000002220446049250313080847263336181640625 d*f= 0.0299999999999999988897769753748434595763683319091796875 0.03= 0.0299999999999999988897769753748434595763683319091796875 d-f= 0.1999999999999999833466546306226518936455249786376953125 0.2= 0.200000000000000011102230246251565404236316680908203125 d/f= 2.999999999999999555910790149937383830547332763671875 (d-f)*(d-f)= 0.03999999999999999389377336456163902767002582550048828125 |
您会注意到
也就是说,0.1和0.3的结果与0.4的结果相同,而0.3-0.1的结果与0.2的结果不同
btw在不使用bigdecimal的情况下对答案进行四舍五入,您可以使用
1 2 3 |
印刷品
1 2 3 | d-f= 0.20 d/f= 3.00 (d-f)*(d-f)= 0.04 |
或
1 2 3 4 5 6 7 |
印刷品
1 2 3 |
舍入消除舍入误差(只留下ToString设计用来处理的表示误差)
在0.1之前和之后可以表示的值可以计算为
1 2 3 | double before_f = Double.longBitsToDouble(Double.doubleToLongBits(f) - 1); System.out.println("The value before 0.1 is" + new BigDecimal(before_f) +" error=" + BigDecimal.valueOf(0.1).subtract(new BigDecimal(before_f))); System.out.println("The value after 0.1 is " + new BigDecimal(f) +" error=" + new BigDecimal(f).subtract(BigDecimal.valueOf(0.1))); |
印刷品
1 2 3 4 | The value before 0.1 is 0.09999999999999999167332731531132594682276248931884765625 error= 8.32667268468867405317723751068115234375E-18 The value after 0.1 is 0.1000000000000000055511151231257827021181583404541015625 error= 5.5511151231257827021181583404541015625E-18 |