为什么添加0.1次多次仍然无损? [英] Why does adding 0.1 multiple times remain lossless?

查看:157
本文介绍了为什么添加0.1次多次仍然无损?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道 0.1 十进制数字不能用有限的二进制数字表示(说明),所以 double n = 0.1 将失去一些精度,并将不完全是 0.1 。另一方面, 0.5 可以正确表示,因为它是 0.5 = 1/2 = 0.1b



说可以理解,添加 0.1 三次将不会准确地提供 0.3 所以以下代码打印 false

 code> double sum = 0,d = 0.1; (int i = 0; i <3; i ++)
$ b sum + = d;
System.out.println(sum == 0.3); //打印false,OK

但是,如何添加 0.1 五次将正好提供 0.5 ?以下代码打印 true

  double sum = 0,d = 0.1; (int i = 0; i <5; i ++)
$ b sum + = d;
System.out.println(sum == 0.5); //打印真的,为什么?

如果 0.1 无法正确表示,是否添加5次可以准确地确定 0.5 ,可以精确地表示?

解决方案

舍入误差不是随机的,它的实现方式是尝试最小化错误。这意味着有时错误是不可见的,或者没有错误。



例如 0.1 不完全是 0.1 ie new BigDecimal(0.1)<新的BigDecimal(0.1) 0.5 正是 1.0 / 2



此程序显示您所涉及的真实值。

  BigDecimal _0_1 = new BigDecimal(0.1 ); 
BigDecimal x = _0_1; (int i = 1; i <= 10; i ++){
System.out.println(i +x 0.1 is+ x +,as double+ x.doubleValue ));
x = x.add(_0_1);
}

打印

  0.1000000000000000055511151231257827021181583404541015625,如双0.1 
0.2000000000000000111022302462515654042363166809082031250,如双0.2
0.3000000000000000166533453693773481063544750213623046875,如双0.30000000000000004
0.4000000000000000222044604925031308084726333618164062500,如双0.4
0.5000000000000000277555756156289135105907917022705078125,如双0.5
0.6000000000000000333066907387546962127089500427246093750,如双0.6000000000000001
0.7000000000000000388578058618804789148271083831787109375,如双0.7000000000000001
0.8000000000000000444089209850062616169452667236328125000,如双0.8
0.9000000000000000499600361081320443190634250640869140625,如双0.9
1.0000000000000000555111512312578270211815834045410156250,如双1.0

注意: 0.3 稍微关闭,但是当你进入 0.4 这些位必须向下移一个以适应53位限制,并且错误被丢弃。再次,对于 0.6 0.7 而是 0.8 1.0 错误被丢弃。


添加5次累积错误,而不是取消它。


出现错误的原因是精度有限。即53位。这意味着随着数字越来越多使用更多的位,位必须从最后掉落。这样做会导致四舍五入,在这种情况下,这是有利的。

当得到一个较小的数字时,你可以得到相反的效果。 0.1-0.0999 => 1.0000000000000286E-4
,您会看到比以前更多的错误。 >

一个例子是为什么在Java 6中为什么Math.round(0.49999999999999994)返回1 在这种情况下,计算中的一点损失会对答案产生很大的影响。


I know the 0.1 decimal number cannot be represented exactly with a finite binary number (explanation), so double n = 0.1 will lose some precision and will not be exactly 0.1. On the other hand 0.5 can be represented exactly because it is 0.5 = 1/2 = 0.1b.

Having said that it is understandable that adding 0.1 three times will not give exactly 0.3 so the following code prints false:

double sum = 0, d = 0.1;
for (int i = 0; i < 3; i++)
    sum += d;
System.out.println(sum == 0.3); // Prints false, OK

But then how is it that adding 0.1 five times will give exactly 0.5? The following code prints true:

double sum = 0, d = 0.1;
for (int i = 0; i < 5; i++)
    sum += d;
System.out.println(sum == 0.5); // Prints true, WHY?

If 0.1 cannot be represented exactly, how is it that adding it 5 times gives exactly 0.5 which can be represented precisely?

解决方案

The rounding error is not random and the way it is implemented it attempts to minimise the error. This means that sometimes the error is not visible, or there is not error.

For example 0.1 is not exactly 0.1 i.e. new BigDecimal("0.1") < new BigDecimal(0.1) but 0.5 is exactly 1.0/2

This program shows you the true values involved.

BigDecimal _0_1 = new BigDecimal(0.1);
BigDecimal x = _0_1;
for(int i = 1; i <= 10; i ++) {
    System.out.println(i+" x 0.1 is "+x+", as double "+x.doubleValue());
    x = x.add(_0_1);
}

prints

0.1000000000000000055511151231257827021181583404541015625, as double 0.1
0.2000000000000000111022302462515654042363166809082031250, as double 0.2
0.3000000000000000166533453693773481063544750213623046875, as double 0.30000000000000004
0.4000000000000000222044604925031308084726333618164062500, as double 0.4
0.5000000000000000277555756156289135105907917022705078125, as double 0.5
0.6000000000000000333066907387546962127089500427246093750, as double 0.6000000000000001
0.7000000000000000388578058618804789148271083831787109375, as double 0.7000000000000001
0.8000000000000000444089209850062616169452667236328125000, as double 0.8
0.9000000000000000499600361081320443190634250640869140625, as double 0.9
1.0000000000000000555111512312578270211815834045410156250, as double 1.0

Note: that 0.3 is slightly off, but when you get to 0.4 the bits have to shift down one to fit into the 53-bit limit and the error is discarded. Again, an error creeps back in for 0.6 and 0.7 but for 0.8 to 1.0 the error is discarded.

Adding it 5 times should cumulate the error, not cancel it.

The reason there is an error is due to limited precision. i.e 53-bits. This means that as the number uses more bits as it get larger, bits have to be dropped off the end. This causes rounding which in this case is in your favour.
You can get the opposite effect when getting a smaller number e.g. 0.1-0.0999 => 1.0000000000000286E-4 and you see more error than before.

An example of this is why in Java 6 Why does Math.round(0.49999999999999994) return 1 In this case the loss of a bit in calculation results in a big difference to the answer.

这篇关于为什么添加0.1次多次仍然无损?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆