我在其他地方讀它,但真的忘了答案,所以我再問這裏。這個循環似乎從來不管最終你在任何語言代碼(我在C#中對其進行測試,C++,Java的...):
double d = 2.0;
while(d != 0.0){
d = d - 0.2;
}
我在其他地方讀它,但真的忘了答案,所以我再問這裏。這個循環似乎從來不管最終你在任何語言代碼(我在C#中對其進行測試,C++,Java的...):
double d = 2.0;
while(d != 0.0){
d = d - 0.2;
}
浮點計算不是完全精確。你會得到一個表示錯誤,因爲0.2沒有精確的表示形式作爲二進制浮點數,所以這個值不會變成,正好等於零。嘗試添加調試語句,看問題:解決這個問題是使用類型decimal
double d = 2.0;
while (d != 0.0)
{
Console.WriteLine(d);
d = d - 0.2;
}
2 1,8 1,6 1,4 1,2 1 0,8 0,6 0,4 0,2 2,77555756156289E-16 // Not exactly zero!! -0,2 -0,4
的一種方式。
爲什麼會出現舍入錯誤?類型'double'存儲二進制數字。因爲0,2寫入的二進制是週期性分數,所以我們必須切斷某些東西來寫入N位的數字。 – adf88 2010-07-28 09:15:42
(一件事你沒有使用相同的變量貫穿始終,但我會認爲這是一個錯字:)
0.2是不是真的0.2。這是最接近double
的值。當你從2.0中減去10次後,你將不會以正好爲 0.0。
在C#中,您可以更改使用decimal
類型,而不是,這將工作:
// Works
decimal d = 2.0m;
while (d != 0.0m) {
d = d - 0.2m;
}
這工作,因爲小數點型確實表示相同的0.2精確十進制值(在一定範圍內,它是一個128位類型)。所涉及的每個值都可以精確地表示出來,因此它很有用什麼不會工作將是這樣的:
decimal d = 2.0m;
while (d != 0.0m) {
d = d - 1m/3m;
}
這裏,「第三」是不能準確地表示,所以我們像以前一樣結束了同樣的問題。
一般來說,在浮點數之間執行精確的相等比較是一個壞主意 - 通常您會在一定的容差範圍內比較它們。
我在C#/。NET上下文中有關於floating binary point和floating decimal point的文章,更詳細地解釋了這些內容。
f爲未初始化;)
如果你的意思是:
double f = 2.0;
這可能是不準確的arthimetic的雙變量的影響。
您最好使用
while(f > 0.0)
*編輯:請參閱下面帕斯卡爾的評論。但是,如果您確實需要運行一個循環,則需要確定的整數次數,而不是使用整數數據類型。
我知道我應該使用它,但爲什麼f!= 0.0不起作用? – 2010-07-28 08:38:30
如果最後一個'f'是2.0e-16,你可能運行一次太多...... – pascal 2010-07-28 08:38:42
這是因爲浮點的精度。使用while(d> 0.0),或者如果您必須,
while (Math.abs(d-0.0) > some_small_value){
}
問題是浮點運算。如果沒有確切的數字二進制表示,那麼您只能存儲最接近的數字(就像您不能在十進制數中存儲數字1/3
一樣 - 您只能存儲類似0.33333333
的數據,長度爲'3')。這意味着浮點數的算術常常不完全準確。嘗試類似如下(JAVA):
public class Looping {
public static void main(String[] args) {
double d = 2.0;
while(d != 0.0 && d >= 0.0) {
System.out.println(d);
d = d - 0.2;
}
}
}
輸出應該是這樣的:
2.0
1.8
1.6
1.4000000000000001
1.2000000000000002
1.0000000000000002
0.8000000000000003
0.6000000000000003
0.4000000000000003
0.2000000000000003
2.7755575615628914E-16
現在你應該能夠明白爲什麼條件d == 0
從未發生過。 (最後一個數字有一個數字,是非常接近於0,但不太
對於浮點古怪的另一個例子,試試這個:
public class Squaring{
public static void main(String[] args) {
double d = 0.1;
System.out.println(d*d);
}
}
因爲有恰好沒有二進制表示0.1
,現蕾也不會產生你所期望的結果(0.01
),但實際上像0.010000000000000002
!
「絕對不準確」誇大了它,IMO。僅僅因爲在二進制浮點中不是每個*十進制值都不能精確表示,所以不會使得精確表示的0.25和0.25的加法精確地達到0.5。 – 2010-07-28 09:09:31
編輯,感謝喬恩 - 這有點誇大了。 – Stephen 2010-07-28 09:29:52
我記得買辛克萊ZX-81,通過優良的基本編程手冊工作我的方式,幾乎返回當我遇到我的第一個浮點舍入錯誤時,我到了商店。
我從來沒有想到人們在27.99998年後仍然會遇到這些問題。
ZX-Spectrum手冊附帶對此問題的詳細說明。我可以記得仔細想想(10歲左右或11歲左右),「哦,這很有道理」。這是一個非常好的手冊... – 2010-07-28 09:36:30
+1爲27.99998年:-) – 2010-07-28 09:41:52
正如其他人所說,這只是你做浮點時得到一個根本性的問題算術到任何基數。只是碰巧基2是計算機中最常見的一個(因爲它承認有效的硬件實現)。
如果可能的話,最好的解決方法是切換到使用循環數的某種商數表示,使得浮點值可以從中得出。好的,這聽起來很誇張!針對您的特殊情況下,我會寫爲:
int dTimes10 = 20;
double d;
while(dTimes10 != 0) {
dTimes10 -= 2;
d = dTimes10/10.0;
}
在這裏,我們真的有分數[20/10,18/10,16/10工作,...,2/10,0/10]在轉換爲浮點數之前,用固定的分母在分子中用整數(即容易得到正確)進行迭代。如果你可以重寫你的真實這樣的迭代,你將會獲得巨大的成功(而且它們確實沒有你之前做的那麼昂貴,這是獲得正確性的一個很好的權衡)。
如果你不能做到這一點,你需要在你的比較中使用相同的內部epsilon。大概,這將取代d != target
與abs(d - target) < ε
,其中ε(ε)選擇有時可能會很尷尬。基本上ε的正確值取決於一系列因素,但最好選擇0。001給出了步長值的比例(即它是步長幅度的百分之五十,因此這裏的任何內容都將是錯誤的而不是信息性的)。
切勿使用具有浮動值的'=='。也許使用像'f> epsilon'這樣的東西。 – pascal 2010-07-28 08:36:03
[這已經過了幾天了,我想我們已經過期了。](http://docs.sun.com/source/806-3568/ncg_goldberg.html) – GManNickG 2010-07-28 08:36:06
在Java中,我認爲有「Strictfp」修飾符對於這種情況 – 2010-07-28 08:36:53