有人可以解釋爲什麼爲什麼Float和Double在添加0.1和0.2時不同?
(0.1::Float) + (0.2::Float) == (0.3::Float)
而
(0.1::Double) + (0.2::Double) /= (0.3::Double)
據我所知,雙應該是更精確。有什麼關於我應該知道的Float?
有人可以解釋爲什麼爲什麼Float和Double在添加0.1和0.2時不同?
(0.1::Float) + (0.2::Float) == (0.3::Float)
而
(0.1::Double) + (0.2::Double) /= (0.3::Double)
據我所知,雙應該是更精確。有什麼關於我應該知道的Float?
Double
確實更精確。是的,您應該瞭解浮點表示的一般情況:您必須非常小心使用它們! ==
通常不太可能有用。您通常會想要使用更專用的函數比較浮點表示,或者至少檢查表達式是否位於某個範圍內,而不是根據內置近似值確定是否具有特定值。
實現的第一件事是,當你進入0.1::Double
和ghci的打印0.1
回來,這只是一個「假象」
Prelude Data.Ratio> 0.1::Double
0.1
爲什麼是幻覺?因爲0.1
實際上不能精確表示爲浮點數!對於Float
和Double
都是如此。注意:
Prelude Data.Ratio> toRational (0.1::Float)
13421773 % 134217728
Prelude Data.Ratio> toRational (0.1::Double)
3602879701896397 % 36028797018963968
所以,在現實中,這些數字的確是「接近」實際的實數0.1
,但也恰恰是0.1
。他們有多接近?讓我們來看看:
Prelude Data.Ratio> toRational (0.1::Float) - (1%10)
1 % 671088640
Prelude Data.Ratio> toRational (0.1::Double) - (1%10)
1 % 180143985094819840
正如你看到的,Double
確實是很多比Float
更精確;作爲Double
的0.1
的表示與實際實數0.1
之間的差異要小得多。但都不是確切的。
因此,確實Double
的添加更加精確,應該優於Float
版本。你看到的令人混淆的平等只不過是四捨五入的奇怪效果罷了。 ==
的結果應該是而不是在浮點土地上值得信賴。實際上,有很多浮點數x
,使得x == x + 1
成立。這裏有一個例子:
Prelude> let x = -2.1474836e9::Float
Prelude> x == x + 1
True
浮點表示很好看的是經典的What Every Computer Scientist Should Know about Floating-Point Arithmetic,這也解釋了許多浮點運算的這些古怪的問題。
另請注意,此行爲對於Haskell並不是唯一的。任何使用IEEE754 Floating-point arithmetic的語言都會採用這種方式,這是現代微處理器實現的標準。
我個人儘量避免使用術語「浮點數」。當你開始看數學時,浮點數表示看起來並不像數字。 「Int」和「Word」等類型當然被用作「Integer」和「Natural」的近似值,但它們本身就是完美守法的交換環。另一方面,浮點表示實際上沒有好的性質:加法和乘法都不是聯合的,乘法不會在加法上分佈。 – dfeuer