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
“a”应该有 21 个元素,但只有 20 个元素。缺少“max - dl”值。我知道浮点数并不精确,但我希望 F# 可以处理它。如果不是,那么为什么 F# 支持带有浮点迭代器的列表推导式?对我来说,这是错误的来源。
Consider the following code:
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
"a" should have 21 elements but has got only 20 elements. The "max - dl" value is missing. I understand that float numbers are not precise, but I hoped that F# could work with that. If not then why F# supports List comprehensions with float iterator? To me, it is a source of bugs.
Online trial: http://tryfs.net/snippets/snippet-3H
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
转换为小数并查看数字,似乎第 21 项会“超出”最大值:
这可能是由于
let dl = 9.5m / 11.m
中缺乏精度?为了消除这种复合错误,您必须使用另一种数字系统,即 Rational。 F# Powerpack 附带一个 BigRational 类,可以像这样使用:
Converting to decimals and looking at the numbers, it seems the 21st item would 'overshoot' max:
That is probably due to lack of precision in
let dl = 9.5m / 11.m
?To get rid of this compounding error, you'll have to use another number system, i.e. Rational. F# Powerpack comes with a BigRational class that can be used like so:
正确处理浮点精度问题可能很棘手。您不应该依赖浮点相等(这就是列表理解对最后一个元素隐式执行的操作)。当您生成无限流时,浮点上的列表推导式非常有用。在其他情况下,您应该注意最后的比较。
如果您想要固定数量的元素,并包含下端点和上端点,我建议您编写这种函数:
当我知道应该包含最后一个元素时,我有时会这样做:
Properly handling float precision issues can be tricky. You should not rely on float equality (that's what list comprehension implicitely does for the last element). List comprehensions on float are useful when you generate an infinite stream. In other cases, you should pay attention to the last comparison.
If you want a fixed number of elements, and include both lower and upper endpoints, I suggest you write this kind of function:
When I know the last element should be included, I sometimes do:
我怀疑问题出在浮点值的精度上。 F# 每次都会将 dl 添加到当前值,并检查当前值是否 <= max。由于精度问题,它可能会跳过 max,然后检查 max+ε <= max(这将产生 false)。因此结果将只有 20 个项目,而不是 21 个。
I suspect the problem is with the precision of floating point values. F# adds dl to the current value each time and checks if current <= max. Because of precision problems, it might jump over max and then check if max+ε <= max (which will yield false). And so the result will have only 20 items, and not 21.
运行你的代码后,如果你这样做:
这意味着 max 大于 a.[19]
如果我们以与范围运算符相同的方式进行计算,但以两种不同的方式分组,然后比较它们:
在这个示例中,您可以看到以不同的顺序添加 7 倍相同的值会产生完全相同的值,但如果我们尝试 8有时结果会根据分组而变化。
你已经做了20次了。
因此,如果您将范围运算符与浮点数一起使用,您应该注意精度问题。
但这同样适用于任何其他带有浮点数的计算。
After running your code, if you do:
It means max is greater than a.[19]
If we do calculations the same way the range operator does but grouping in two different ways and then compare them:
In this sample you can see how adding 7 times the same value in different order results in exactly the same value but if we try it 8 times the result changes depending on the grouping.
You're doing it 20 times.
So if you use the range operator with floats you should be aware of the precision problem.
But the same applies to any other calculation with floats.