Swift随机浮点数致命错误:无法在无限范围上实现均匀分布

huangapple go评论46阅读模式
英文:

Swift Random Float Fatal Error : no uniform distribution on an infinite range

问题

以下是已翻译的内容:

"我正在尝试生成一个完全随机的float值,这应该很容易。但是,它不起作用,看起来也许我是第一个遇到这个错误的人?我确实找不到任何先前的帖子。

这段代码:

let min = -Float.greatestFiniteMagnitude
let max = +Float.greatestFiniteMagnitude
let range = min...max
let random = Float.random(in: range)

在最后一行之前是有效的,但在最后一行失败,产生Swift/FloatingPointRandom.swift:162: Fatal error: There is no uniform distribution on an infinite range错误。

然而,范围不应该是无限的。在Playground中检查min和max或范围本身,可以看到边界是-3.402823e+383.402823e+38,都不是无限的。

到底发生了什么?是否有我错过的正确方法?除了以下方法,对于这样一个简单的任务来说,似乎非常复杂:

let min = Float(0)
let max = Float.greatestFiniteMagnitude
let range = min...max
let signs: [Float] = [-1, 1]
let random = Float.random(in: range) * signs.randomElement()!
英文:

I'm trying to generate a completely random float value, which should be pretty easy. However, it doesn't work, and by the looks of it maybe I'm the first person to come across this error ? I certainly can't seem to find any prior post about it.

This code :

let min = -Float.greatestFiniteMagnitude
let max = +Float.greatestFiniteMagnitude
let range = min...max
let random = Float.random(in: range)

Works until the last line, which fails, producing Swift/FloatingPointRandom.swift:162: Fatal error: There is no uniform distribution on an infinite range

However, the range shouldn't be infinite. Inspecting min and max or the range itself in the playground shows that the bounds are -3.402823e+38 to 3.402823e+38, neither of which are infinite.

What is going on ? And is there a proper way to do this that I missed ? Besides the following, which seems very convoluted for such a simple task :

let min = Float(0)
let max = Float.greatestFiniteMagnitude
let range = min...max
let signs : [Float] = [-1, 1]
let random = Float.random(in: range) * signs.randomElement()!

答案1

得分: 2

这是当前实现浮点数随机数生成的限制之一 - 如果你查看stdlib/public/core/FloatingPointRandom.swift:156(就在你的错误消息指向的地方附近),你可以看到这个限制的来源:

let delta = range.upperBound - range.lowerBound
//  TODO: this still isn't quite right, because the computation of delta
//  can overflow (e.g. if .upperBound = .maximumFiniteMagnitude and
//  .lowerBound = -.upperBound); this should be re-written with an
//  algorithm that handles that case correctly, but this precondition
//  is an acceptable short-term fix.
_precondition(
  delta.isFinite,
  "There is no uniform distribution on an infinite range"
)

该代码试图防止在边界无效的情况下生成随机数,但不能充分处理溢出。有一个GitHub问题追踪这个错误,但似乎已经存在一段时间了 - 尽管有一条评论表示这将在Swift 5中修复,但这个修复尚未引入。

也许值得在那个问题上发表评论,或者在Swift论坛上提出这个问题,那里的工程师可以看到这个问题并加以解决。


与此同时,你提出的解决方案应该足够接近某种定义下的 "足够接近":你将得到一个在你想要的范围内的随机数,尽管浮点数精度的限制可能会影响你得到的数字的精确分布。根据你的用例,这可能并不重要,但在不了解更多情况的情况下,这可能会或可能不会在完全修复引入之前对你起作用。

你还可以尝试在比 [-1, 1] 范围大得多的范围内生成随机数,并将其缩小,这可能会使你得到更准确的值分布。

英文:

This is a limitation of the current implementation of random number generation for floating point values — if you check out stdlib/public/core/FloatingPointRandom.swift:156 (right around where your error message points to), you can see where this limitation comes from:

let delta = range.upperBound - range.lowerBound
//  TODO: this still isn't quite right, because the computation of delta
//  can overflow (e.g. if .upperBound = .maximumFiniteMagnitude and
//  .lowerBound = -.upperBound); this should be re-written with an
//  algorithm that handles that case correctly, but this precondition
//  is an acceptable short-term fix.
_precondition(
  delta.isFinite,
  "There is no uniform distribution on an infinite range"
)

The code attempts to prevent generating random numbers if the bounds aren't valid, but doesn't adequately handle overflows. There's a GitHub issue tracking this bug, but it appears that it's been around for a while now — although there's a comment stating that this would be fixed for Swift 5, that fix hasn't been introduced.

It may be worth commenting on that issue, or raising this on the Swift forums, where engineers who could address the issue can see this.


In the meantime, your proposed solution should get you close enough, for some definition of "close enough": you'll get a random number within the range that you want, though the limitations of floating-point precision may affect the exact distribution of numbers you get out. Depending on your use-case, this may not really matter, but without knowing more, this may or may not work for you until a full fix is introduced.

You could also attempt to generate a random number within a much larger range than [-1, 1], and scale it by a smaller amount, which may get you a more accurate distribution of values.

huangapple
  • 本文由 发表于 2023年2月16日 10:15:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/75467199.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定