可能重复:
Haskell 范围和浮点数
例如,当我输入
[0.1, 0.3 ..1]
我明白了:
[0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]
我期望:
[0.1,0.3,0.5,0.7,0.9]
可能重复:
Haskell 范围和浮点数
例如,当我输入
[0.1, 0.3 ..1]
我明白了:
[0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]
我期望:
[0.1,0.3,0.5,0.7,0.9]
尝试
map (/10) [1, 3 .. 10]
反而。
问题是浮点数使用二进制分数,而二进制分数不能精确地表示十进制分数。因此,您会遇到错误并且错误会累积。
二进制分数不能精确表示 1/5,就像十进制分数不能精确表示 1/3 一样——我们能做的最好的就是 0.33333....
[0.1, 0.3 .. 1]
是简写
[0.1,
0.1 + 0.2,
0.1 + 0.2 + 0.2,
0.1 + 0.2 + 0.2 + 0.2,
0.1 + 0.2 + 0.2 + 0.2 + 0.2,
0.1 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2]
另一个问题是,当下一个元素超过限制的一半以上时,列表将在第一个等于或超过限制的元素之后停止,这就是你拥有该元素的原因。1.0999999999999999
该表达式[e1, e2 .. e3]
被评估为enumFromThenTo e1 e2 e3
,对于浮点数意味着(来自The Haskell 98 Report):
对于 Float 和 Double, enumFrom 系列的语义由上面的 Int 规则给出,除了当元素变得大于
e3+i/2
正增量 i 或当它们小于e3+i/2
负 i 时列表终止。
这意味着浮点数的最后一个元素[e1, e2 .. e3]
通常大于e3
,并且可以达到e3+(e2-e1)/2 - ε
。
这是浮点数在计算机中如何表示的问题。带有浮点数的简单算术通常不会按预期运行。重复算术会累积“舍入误差”,这意味着当您重复添加数字(例如)时,结果会逐渐变差。
在某些情况下,您可以通过使用不同的数字表示来避免这些问题。例如,如果您只关心有理数,则可以使用Rational
类型。所以你可以这样做:
[0.1,0.3..1] :: [Rational]
这导致:
[1 % 10,3 % 10,1 % 2,7 % 10,9 % 10,11 % 10]
这是没有舍入误差的正确答案;每个数字仅表示为两个Integer
s 的比率。根据您的具体情况,这可能是比使用浮点数更好的选择。
这仍然超出了上限,但这比从浮点数中得到的舍入误差更容易处理。
请注意,对于某些性能关键的浮点数可能会更快。