F# 中带有浮点迭代器的列表推导式

发布于 2024-10-26 01:57:29 字数 423 浏览 3 评论 0原文

考虑以下代码:

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# 支持带有浮点迭代器的列表推导式?对我来说,这是错误的来源。

在线试用:http://tryfs.net/snippets/snippet-3H

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦旅人picnic 2024-11-02 01:57:29

转换为小数并查看数字,似乎第 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# Powerpack 附带一个 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

Converting to decimals and looking at the numbers, it seems the 21st item would 'overshoot' max:

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

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:

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
锦欢 2024-11-02 01:57:29

正确处理浮点精度问题可能很棘手。您不应该依赖浮点相等(这就是列表理解对最后一个元素隐式执行的操作)。当您生成无限流时,浮点上的列表推导式非常有用。在其他情况下,您应该注意最后的比较。

如果您想要固定数量的元素,并包含下端点和上端点,我建议您编写这种函数:

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 ]

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:

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

When I know the last element should be included, I sometimes do:

let a = [ for z in min .. dl .. max + dl*0.5 -> z ]
谁把谁当真 2024-11-02 01:57:29

我怀疑问题出在浮点值的精度上。 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.

捶死心动 2024-11-02 01:57:29

运行你的代码后,如果你这样做:

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

这意味着 max 大于 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次了。

因此,如果您将范围运算符与浮点数一起使用,您应该注意精度问题。
但这同样适用于任何其他带有浮点数的计算。

After running your code, if you do:

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

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:

> 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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文