2015-04-23 108 views
4

考慮下面的代碼:浮點數,精度,秒差距

import Text.Parsec 
import Text.Parsec.Language 
import Text.Parsec.String 
import qualified Text.Parsec.Token as Token 

float :: Parser Double 
float = Token.float (Token.makeTokenParser emptyDef) 

myTest :: String -> Either ParseError Double 
myTest = parse float "" 

現在,由於快速檢查我知道一個神奇的數字(我已經對準結果爲 方便):

λ> myTest "4.23808622486133" 
Right  4.2380862248613305 

某些浮點數不能在內存中精確表示,有些操作很容易將「波動」引入浮點數。我們 都知道。但是,這個解析問題的原因似乎有所不同。

有關幫助我發現此功能的測試的幾句話。簡單地說, 在這些測試中浮點值被生成,打印並解析回 (帶有Parsec)。例如,數字9.2被稱爲impossible to represent as floating point value, 然而它通過了測試(顯然是因爲「智能」打印 功能)。爲什麼4.23808622486133失敗?


對於那些誰相信這些數字都是相同的,4.238086224861334.2380862248613305只是簡短明確表示:

a1 :: Double 
a1 = 9.2000000000000003 

a2 :: Double 
a2 = 9.200000000000001 

b1 :: Double 
b1 = 4.23808622486133 

b2 :: Double 
b2 = 4.2380862248613305 

現在:

λ> a1 == a2 
True 
λ> b1 == b2 
False 
+1

有些人會再次不同意,但我會一直說:當你使用浮點時,你應該永遠不要期望任何東西都是完全相等的。 – leftaroundabout

+1

@leftaroundabout,絕對正確。然而,有趣的是爲什麼這種事情在解析中發生。我的意思是,假設有一個數字「x」不能完全表示爲浮點數,但當它被打印時,假設有實際打印的「x」號。當你解析它時,你應該再次得到'x'。我期望在那裏有某種一致性...... – Mark

+0

我很遺憾寫下了我的答案。你可以不接受嗎?另一個對Haskell程序員來說似乎更有用。 –

回答

0

這在Parsec中仍然沒有修復。如果這個確切的問題打破了你的一天,看看Megaparsec,這是Parsec的一個分支,它修復了許多錯誤和概念缺陷,提高了錯誤信息的質量等等。

正如你可以看到這個問題是有固定的:

λ> parseTest float "4.23808622486133" 
4.23808622486133 
λ> parseTest float "4.2380862248613305" 
4.2380862248613305 

披露:我是百萬秒差距的作者之一。

+0

在SO上討論您自己的項目/軟件包時,您需要向他們透露您的連接。 – dfeuer

+0

@dfeuer,完成:現在應該清楚了。 – Mark

3

秒差距確實使用到雙轉換什麼相當於

foldr (\d acc -> read [d] + acc/10) 0 "423808622486133" :: Double 

,正如你指出的,這不等於

423808622486133/100000000000000 :: Double 

我同意,這應該算是秒差距的錯誤。