2011-03-25 75 views
2

浮動迭代器列表解析請看下面的代碼:在F#

let dl = 9.5/11. 
let min = 21.5 + dl 
let max = 40.5 - dl 

let a = [ for z in min .. dl .. max -> z ] // should have 21 elements 
let b = a.Length 

「一」應該有21個元素,但也只拿到了20元。 「max-dl」值缺失。我知道浮點數不是精確的,但我希望F#可以用它來工作。如果不是,那麼爲什麼F#支持使用float迭代器的List comprehensions?對我而言,這是一個錯誤的來源。

在線試用:http://tryfs.net/snippets/snippet-3H

+2

「對我來說,這是一個錯誤的來源。」 浮游物是錯誤的來源。當你使用浮點值時,無論你的語言是什麼,都需要注意。列表理解只不過是一系列的增加和比較。 – Laurent 2011-03-25 11:50:39

+0

同意。我不認爲允許使用浮動列表理解是一個好主意。我相信這將需要特殊處理來禁用它,這就是爲什麼它被允許。 – 2011-03-25 13:37:42

+1

我發現舊電子郵件在2008年10月我們決定削減此功能,贊成linspace運營商。但隨後時間/優先次序/分類方式開始啓用,並且此功能從未被移除/替換。好吧。 – Brian 2011-03-25 23:26:07

回答

0

我懷疑問題是浮點值的精度。 F#每次將dl添加到當前值,並檢查當前是否爲< = max。由於精度問題,它可能跳過最大值,然後檢查max +ε< = max(這會產生錯誤)。這樣一來,結果將只有20個項目,而不是21

+0

是的,我同意,但隨後使用浮點數列表解析是一個非常糟糕的功能和錯誤的來源... – 2011-03-25 08:40:56

+0

在這種特殊情況下,用小數代替所有浮點數會得到相同的結果。 – ildjarn 2011-03-25 08:44:16

+0

@ildjarn:你能舉個例子嗎? – 2011-03-25 08:51:02

0

運行代碼後,如果你這樣做:

> compare a.[19] max;; 
val it : int = -1 

這意味着最大大於a [19]

如果我們這樣做的計算以同樣的方式的範圍內操作者,但在兩種不同的方式進行分組,然後對它們進行比較:

> compare (21.5+dl+dl+dl+dl+dl+dl+dl+dl) ((21.5+dl)+(dl+dl+dl+dl+dl+dl+dl));; 
val it : int = 0 
> compare (21.5+dl+dl+dl+dl+dl+dl+dl+dl+dl) ((21.5+dl)+(dl+dl+dl+dl+dl+dl+dl+dl));; 
val it : int = -1 

在此示例中,您可以看到如何添加7次不同的順序結果相同的值完全相同的值,但如果我們嘗試它8次結果的變化取決於分組。

你正在做20次。

所以,如果你使用帶浮點數的範圍運算符,你應該知道精度問題。 但這同樣適用於其他使用浮點數的計算。

2

轉換爲小數,看着這些數字,似乎21項將「超調」的最大:

let dl = 9.5m/11.m 
let min = 21.5m + dl 
let max = 40.5m - dl 

let a = [ for z in min .. dl .. max -> z ] // should have 21 elements 
let b = a.Length 

let lastelement = List.nth a 19 
let onemore = lastelement + dl 
let overshoot = onemore - max 

這可能是由於在let dl = 9.5m/11.m缺乏精確度?

要擺脫這種複合錯誤,您必須使用另一個數字系統,即Rational。 F#動力組配備了可像這樣使用一個BigRational類:

let dl = 95N/110N 
let min = 215N/10N + dl 
let max = 405N/10N - dl 

let a = [ for z in min .. dl .. max -> z ] // Has 21 elements 
let b = a.Length 
1

妥善處理float精度的問題可能會非常棘手。你不應該依賴於浮點數的平等(這是列表理解爲最後一個元素所隱含的)。當你生成一個無限流時,在float上列表理解是很有用的。在其他情況下,您應該注意最後的比較。

如果你想要的元素一個固定的號碼,包括較低和較高終點,我建議你寫這樣的功能:

let range from to_ count = 
    assert (count > 1) 
    let count = count - 1 
    [ for i = 0 to count do yield from + float i * (to_ - from)/float count] 

range 21.5 40.5 21 

當我知道最後一個元素應該被包括在內,我有時會做:

let a = [ for z in min .. dl .. max + dl*0.5 -> z ]