2011-09-17 119 views
26

我有一個雙重"138630.78380386264",我想將它轉換爲小數,但是當我這樣做時,我通過鑄造或使用Convert.ToDecimal()來做它,並且我失去了精度。C#double至十進制精度損失

發生了什麼事?十進制和雙能容納這個數字:

enter image description here

double doub = double.Parse("138630.78380386264"); 
decimal dec = decimal.Parse("138630.78380386264"); 
string decs = dec.ToString("F17"); 
string doubse =DoubleConverter.ToExactString(doub); 
string doubs = doub.ToString("F17"); 

decimal decC = (decimal) doub; 
string doudeccs = decC.ToString("F17"); 
decimal decConv = Convert.ToDecimal(doub); 
string doudecs = decConv.ToString("F17"); 

另外:我怎麼可以得到雙倍的ToString()打印出相同的結果調試器顯示?例如138630.78380386264

+0

[將C#中的小數轉換爲雙精度值導致差異]的可能重複(http://stackoverflow.com/questions/1584314/conversion-of-a-decimal-to-double-number-in- c-results-in-a-difference) –

+1

那麼Convert.ToDecimal(somedouble)恰好等於(decimal)somedouble,所以沒有什麼意外。我不知道爲什麼這個演員輪到最後一個數字。 – harold

+0

@harold你被騙了。它圓到最近。雙值完成386264和15 sig無花果小數結束3863. –

回答

23

138630.78380386264不能精確表示爲雙精度。最接近的雙精度數字(發現here)是138630.783803862635977566242218017578125,這與您的發現一致。

你問爲什麼轉換爲十進制不包含更多的精度。 documentation for Convert.ToDecimal()有答案:

此方法返回的十進制值包含最多15個有效數字。如果值參數包含超過15個有效數字,則使用四捨五入將其舍入到最近。以下示例說明了Convert.ToDecimal(Double)方法如何使用舍入到最近來返回一個十進制值與15位有效數字。

雙數值,四捨五入到最接近的15個有效數字是138630.783803863,正如您在上面顯示的一樣。

+0

那麼爲什麼轉換到十進制轉換更多的精度呢? – GreyCloud

+0

好的感謝解釋。無論如何要讓ToString打印出超過15個無花果? - 我希望能夠回到原來的字符串 - 這樣我就可以得到Double來解析字符串,並在不失去精度的情況下進行轉換 – GreyCloud

+0

'doub.ToString(「G17」)'會給你相同的字符串調試器產生。但我不確定調試器在做什麼。由於'double'精度有限,因此通常不能解析字符串,然後返回原始字符串。如果您需要知道原始字符串,請記住它。 –

4

這是一個不幸,我想。近139,000,Decimal精度比Double好得多。但仍然因爲這個問題,我們有不同Double s被投射到相同Decimal。例如

double doub1 = 138630.7838038626; 
double doub2 = 138630.7838038628; 
Console.WriteLine(doub1 < doub2);     // true, values differ as doubles 
Console.WriteLine((decimal)doub1 < (decimal)doub2); // false, values projected onto same decimal 

事實上,有不同的可表示Double和上述doub2之間doub1,所以它們是不一樣的。

這裏是一個有些傻工作aronud:

static decimal PreciseConvert(double doub) 
{ 
    // Handle infinities and NaN-s first (throw exception) 
    // Otherwise: 
    return Decimal.Parse(doub.ToString("R"), NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint); 
} 

"R"格式字符串確保包含足夠的額外的數字,使映射射(在Decimal具有優異的精度域)。


注意,在一些範圍內,一個longInt64)具有精密稱優於的Double。所以我檢查了轉換是否以相同的方式進行(首先舍入到15位重要小數位)。他們不是!所以:

double doub3 = 1.386307838038626e18; 
double doub4 = 1.386307838038628e18; 

Console.WriteLine(doub3 < doub4);    // true, values differ as doubles 
Console.WriteLine((long)doub3 < (long)doub4); // true, full precision of double used when converting to long 

似乎不一致使用不同的「規則」,如果目標是decimal

請注意,因此,(decimal)(long)doub3產生的結果比(decimal)doub3更準確。

+0

問題不在於139,000附近的數字是因爲雙精度是16位數字炎,如果數字位於分隔符的左側或右側,則無關緊要,如果減少此數字到13.86307838038626你會有同樣的問題,十進制的精度爲32位,這就是爲什麼你沒有這個問題。 –

+0

@ GabrielVonlantenC.Lopes你是對的這個問題並不特定於139,000附近的數字。我只把這個數字看作是一個例子,因爲那是原始問題中的數字。實際上(非常粗略地)'1e-12'和'1e + 29'之間的所有數字都具有完全相同的問題。在此範圍之外,「decimal」或者沒有被定義,或者不再具有比「double」更高的精度。 (但是,'decimal'的精度爲28-29位。) –