The discussion section of the documentation of
static func random(in range: Range<Float>) -> Float
says:
The random() static method chooses a random value from a continuous uniform distribution in range, and then converts that value to the nearest representable value in this type. Depending on the size and span of range, some concrete values may be represented more frequently than others.
Unless I'm misinterpreting this, the process described would let any representable value within the given range be chosen.
However, this is not the case, as the following program demonstrates:
let range = Float(0) ..< Float(1 << 24)
var lowSamples = [Float]()
while lowSamples.count < 10 {
let v = Float.random(in: range)
if v < 100 { lowSamples.append(v) }
}
print(lowSamples)
Here's the output of a couple of runs:
[3.0, 48.0, 30.0, 82.0, 36.0, 84.0, 74.0, 5.0, 94.0, 95.0]
[95.0, 24.0, 58.0, 19.0, 53.0, 23.0, 47.0, 43.0, 11.0, 99.0]
[4.0, 49.0, 56.0, 2.0, 90.0, 51.0, 64.0, 79.0, 92.0, 21.0]
If, like the documentation says, this method "chooses a random value from a continuous uniform distribution in range, and then converts that value to the nearest representable value in this type", then how come the fractional part of every value is always zero?
We can see why by looking at the implementation, which is doing essentially this:
let unitRandom = Float(rndUInt64Value >> (32+8)) * Float(0.5).ulp
return unitRandom * (range.upperBound - range.lowerBound) + range.lowerBound
So, since in my example range = Float(0) ..< Float(1 << 24)
, and range.upperBound.nextDown.ulp == 1.0
, the method will only be able to choose among values from [0.0, 1.0, ... Float(1 << 24 - 1)]
, and never eg 0.5
.
This means that for eg range = Float(0) ..< Float(1 << 25)
, it will only return even values (since range.upperBound.nextDown.ulp == 2.0
).
Question:
Should the implementation change to match the documentation or should the documentation change to match the implemented behavior?
(Related to this thread, which never got a clear answer.)