2013-10-26 43 views
1

我注意到一些奇怪的行爲,同時添加兩個雙打,有時它工作正確,有時不是!C#爲一個簡單的數學運算獲得奇怪的結果

這是第一個例子:

double num1 = 0.1 + 0.7; //Guess the result? 

容易 - 0.8!或不?

看看奇怪的結果:

enter image description here

你猜怎麼着,if語句的else塊裏面去,並打印num1 - 不,它不打印0.799999999999993,它打印0.8。

所以我一直向前走了一步,並試圖將此代碼:

if (0.1 + 0.7 == 0.8) //Returns false ?? 
{ 
    Console.WriteLine("Correct"); 
} 

OK,奇怪的,但現在我找到了正確的方法,就應該使用F(浮動)。正如我記得double有很多空格,所以它可以包含更多的數字,也許這是原因。

float num2 = 0.1f + 0.7f; 

if (num2 == 0.8f) //Perfect - finally works !!! 
{ 
    Console.WriteLine("Correct"); 
} 
else 
{ 
    Console.WriteLine(num2); 
} 

但是現在,我試試這個 - 並且它又返回false,爲什麼?

if (0.1f + 0.7f == 0.8f) //Returns false :(
{ 
    Console.WriteLine("Correct"); 
} 

手錶結果調試它的時候:

enter image description here

誰能給我解釋一下什麼是錯在這裏?那些錯誤?

在此先感謝。

+0

的可能重複(http://stackoverflow.com/questions/753948/why-is-floating-point-arithmetic-in-c-sharp-imprecise) – MichaC

+0

@MichaC提問的方式完全不同,也不一樣。 –

+0

因爲你在這個問題上付出了很多努力,但最終它是一回事。這僅僅是因爲C#中的浮點運算不精確。在這個網站上已經回答了1000次^^ – MichaC

回答

4

浮點運算不精確。您應該閱讀文章What Every Computer Scientist Should Know About Floating-Point Arithmetic。如果您希望計算精確到十進制,則應使用decimal類型,其中0.1m + 0.7m == 0.8mtrue。您應該注意,decimal使用的浮點數與floatdouble一樣,除了基數爲10,而不是2.由於處理基數2更容易且高度優化,所以decimal要慢得多。它也有它自己的不準確之處,只有在處理可以以小數位數表示的數字(如0.70.1)時纔是精確的,這對財務數據非常有用。

理解浮點數的另一篇有用的文章是Wikipedia's Double-precision floating-point format

+0

+1的鏈接:) – oerkelens

1

在內部,double以base-2的位數存儲。 所有的整數可以完全用base-2表示,但是當涉及到分數時,它是另一個問題。

與我們不能精確地在我們的十進制系統中精確地再現1/3的結果相同,在二進制系統中,您不能表示所有分數。由於十進制是10的基數,很容易用十進制表示x/10,但在基數2中,某些x不可能。

因此,無論何時使用雙打,都要注意一些舍入誤差。

由於這個原因,只要需要確切的數字就不會使用雙打,就像貨幣金額一樣。

+0

在第二段中,當然你的意思是「對某些'x'不可能」。例如,5/10 = 1/2可以精確地表示爲基數2的浮點數。 – delnan

+0

事實上,你是對的,我編輯它:) – oerkelens

+0

但聲明是正確的:在二進制0.1有相同的問題,1/3有小數。 – duffymo

1

調用Math.Round方法以確保兩個值具有相同的精度。下面的示例修改前面的例子中,使得兩個分數值等同於使用這種方法:

using System; 

public class Example 
{ 
    public static void Main() 
    { 
     double value1 = .333333333333333; 
     double value2 = 1.0/3; 
     int precision = 7; 
     value1 = Math.Round(value1, precision); 
     value2 = Math.Round(value2, precision); 
     Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2)); 
    } 
} 
// The example displays the following output: 
//  0.3333333 = 0.3333333: True 
1

下面是答案:http://www.codeproject.com/Articles/383871/Demystify-Csharp-floating-point-equality-and-relat

浮動值僅僅是近似值。要比較兩個浮點值,您需要檢查它們之間的差異。該差異不能大於float.Epsilon

使用此對這些值進行比較:?在C#不精確爲什麼浮點運算]

bool AlmostEqualDoubles(double nVal1, double nVal2, int nPrecision = 6) 
{ 
    nPrecision = Math.Max(Math.Min(16, nPrecision), 0); 
    double nEpsilon = 1.0; 
    for (int i = 0; i < nPrecision; i++) nEpsilon *= 0.1;  
    return (nVal2 - nEpsilon) < nVal1 && nVal1 < (nVal2 + nEpsilon); 
} 
+0

「浮動值只是近似值」並不嚴格。說明這一點的更好方法是許多十進制值不會精確映射到IEEE浮點數。但是,當映射到浮點數時(例如4,2,1,5 .25,.125等),可以使用2的冪分解的值將具有精確的值。在數字如1/3等的小數點世界中會出現同樣的問題。 – spender