問題描述
我知道 0.1
十進制數不能用有限的二進制數精確表示(explanation),所以 double n = 0.1
會損失一些精度,不會完全是 0.1
.另一方面,0.5
可以精確表示,因為它是 0.5 = 1/2 = 0.1b
.
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
.
話雖如此,添加 0.1
三次 不會給出完全正確的 0.3
是可以理解的,所以下面的代碼打印出 false代碼>:
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
但是,如何添加 0.1
五次 會恰好得到 0.5
?以下代碼打印 true
:
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?
如果0.1
不能準確表示,怎么加5次就正好0.5
可以準確表示?
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.
例如 0.1
不完全是 0.1
即 new BigDecimal("0.1") <new BigDecimal(0.1)
但 0.5
正是 1.0/2
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);
}
打印
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
注意:0.3
稍微偏離了一點,但是當您到達 0.4
時,位必須向下移動一位以適應 53 位限制,并且錯誤是丟棄.同樣,0.6
和 0.7
的錯誤再次出現,但對于 0.8
到 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.
添加 5 次應該會累積錯誤,而不是取消它.
Adding it 5 times should cumulate the error, not cancel it.
出現錯誤的原因是精度有限.即53位.這意味著隨著數字變大使用更多位,必須從末尾丟棄位.這會導致舍入,在這種情況下對您有利.
當獲得較小的數字時,您可以獲得相反的效果,例如0.1-0.0999
=> 1.0000000000000286E-4
你會看到比以前更多的錯誤.
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.
這方面的一個例子是為什么在 Java 6 為什么 Math.round(0.49999999999999994) return 1 在這種情況下,計算中丟失一個位會導致答案有很大差異.
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仍然無損?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!