[英]Converting float to double loses precision C#
我有以下代碼
double temp3 = 61.01;
//This can actually be various other types, but float is the one that causes problems
dynamic temp = 61.01f;
double temp2 = (double)Convert.ChangeType(temp, typeof(double));
double newValue = temp2 - temp3;
//newValue should == 0 but it does not
Console.WriteLine(String.Format(" {0:F20}", temp));
Console.WriteLine(String.Format(" {0:F20}", temp2));
Console.WriteLine(String.Format(" {0:F20}", temp3));
Console.WriteLine(String.Format(" {0:F20}", newValue));
哪個產生
61.01000000000000000000
61.00999832153320000000
61.01000000000000000000
-0.00000167846679488548
為什么Convert.ChangeType失去精度?
我們使用Convert.ChangeType是因為使用了動態變量,該變量可以是byte / uint / float / double等
在此問題中觀察到的問題很大程度上是由Microsoft選擇格式引起的,尤其是Microsoft軟件無法顯示准確的值,因為即使格式字符串要求輸入更多的數字,它也會限制用於轉換為十進制的位數。 此外,轉換float
時使用的位數少於轉換double
時使用的位數。 因此,如果對具有相同值的float
和double
進行格式化,則結果可能會有所不同,因為float
格式將使用較少的有效數字。
下面,我一步一步地研究問題中的代碼語句。 綜上所述,問題的關鍵是,值61.0099983215332被格式化為“61.0100000000000”,當它是一個float
和“61.0099983215332”,當它是一個double
。 這純粹是Microsoft的格式選擇,並非由浮點運算的性質引起。
語句double temp3 = 61.01
將temp3
初始化為正好61.00999999999999801048033987171947956085205078125。 由於二進制浮點格式的性質,必須從61.01開始進行此更改-它不能精確表示61.01,因此使用可表示為double
的最接近值。
語句dynamic temp = 61.01f
將temp
精確地初始化為61.009998321533203125。 與double
,已使用了最接近的可表示值,但是由於float
精度較低,因此最接近的值不如double
情況下那樣接近。
語句double temp2 = (double)Convert.ChangeType(temp, typeof(double));
將temp
轉換為與temp
具有相同值的double
,因此其值為61.009998321533203125。
語句double newValue = temp2 - temp3;
正確地減去兩個值,得出准確的結果0.00000167846679488548033987171947956085205078125,沒有錯誤。
語句Console.WriteLine(String.Format(" {0:F20}", temp));
格式化名為temp
的float
。 格式化float
涉及對Single.ToString的Single.ToString
。 微軟的文檔有點含糊。 它說,默認情況下,僅返回七(十進制)個精度數字。 它說使用G
或R
格式最多獲取9個,而F20
既不使用G
也不使用R
因此,我相信只使用了七個數字。 當61.009998321533203125四舍五入到七個有效十進制數字時,結果為“ 61.01000”。 然后, ToString
方法將此值填充到小數點后的20位,從而產生“ 61.01000000000000000000”。
接下來,我將介紹您的第三條WriteLine
語句,然后再返回第二條。
語句Console.WriteLine(String.Format(" {0:F20}", temp3));
格式化名為temp3
的double
temp3
。 由於temp3
是double
,因此將調用Double.ToString
。 此方法使用15位精度(除非使用G
或R
)。 將61.00999999999999801048033987171947956085205078125舍入為15個有效十進制數字時,結果為“ 61.0100000000000”。 然后, ToString
方法將此值填充到小數點后的20位,從而產生“ 61.01000000000000000000”。
語句Console.WriteLine(String.Format(" {0:F20}", temp2));
格式化名為temp2
的double
temp2
。 temp2
是一個double
值,其中包含float
temp
中的值,因此它包含61.009998321533203125。 將其轉換為15個有效十進制數字時,結果為“ 61.0099983215332”。 然后, ToString
方法將此值填充到小數點后的20位,從而產生“ 61.00999832153320320000000”。
最后,語句Console.WriteLine(String.Format(" {0:F20}", newValue));
格式化newValue
。 將.00000167846679488548033987171947956085205078125格式化為15個有效數字會產生“ 0.00000167846679488548”。
這是因為浮點數分配給它們的位數少於雙精度數。 浮點數通常有23位,而雙精度數通常有52位。因此,通過轉換,您基本上是在砍掉位,直到可以將其放入浮點數為止。 因此,這就是您降低精度的原因。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.