Skip to content

Commit 12419e4

Browse files
committed
Correct bias in random float sampling.
Fixes JuliaLang#33222. Also changed some hardcoded test values accordingly.
1 parent 51b3227 commit 12419e4

File tree

3 files changed

+23
-13
lines changed

3 files changed

+23
-13
lines changed

stdlib/Random/src/Random.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,8 @@ Pick a random element or array of random elements from the set of values specifi
309309
* an `AbstractDict` or `AbstractSet` object,
310310
* a string (considered as a collection of characters), or
311311
* a type: the set of values to pick from is then equivalent to `typemin(S):typemax(S)` for
312-
integers (this is not applicable to [`BigInt`](@ref)), to ``[0, 1)`` for floating
313-
point numbers and to ``[0, 1)+i[0, 1)]`` for complex floating point numbers;
312+
integers (this is not applicable to [`BigInt`](@ref)), to ``(0, 1)`` for floating
313+
point numbers and to ``(0, 1)+i(0, 1)]`` for complex floating point numbers;
314314
315315
`S` defaults to [`Float64`](@ref).
316316

stdlib/Random/src/generation.jl

+16-7
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,26 @@ Sampler(::Type{RNG}, ::Type{T}, n::Repetition) where {RNG<:AbstractRNG,T<:Abstra
2222
# generic random generation function which can be used by RNG implementors
2323
# it is not defined as a fallback rand method as this could create ambiguities
2424

25-
rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01{Float16}}) =
26-
Float16(reinterpret(Float32,
27-
(rand(r, UInt10(UInt32)) << 13) | 0x3f800000) - 1)
25+
function rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01{Float16}})
26+
# bias correction below is equivalent to adding eps(Float16)/2, see #33222
27+
z = reinterpret(Float32, (rand(r, UInt10(UInt32)) << 13) | 0x3f800000)
28+
Float16(z - prevfloat(Float16(1)))
29+
end
2830

29-
rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01{Float32}}) =
30-
reinterpret(Float32, rand(r, UInt23()) | 0x3f800000) - 1
31+
function rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01{Float32}})
32+
# bias correction below is equivalent to adding eps(Float32)/2, see #33222
33+
reinterpret(Float32, rand(r, UInt23()) | 0x3f800000) - prevfloat(1f0)
34+
end
3135

32-
rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen12_64}) =
36+
function rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen12_64})
37+
# NOTE this has a small bias of eps()/2, see discussion in #33222
3338
reinterpret(Float64, 0x3ff0000000000000 | rand(r, UInt52()))
39+
end
3440

35-
rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01_64}) = rand(r, CloseOpen12()) - 1.0
41+
function rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01_64})
42+
# bias correction below is equivalent to adding eps()/2, see #33222
43+
rand(r, CloseOpen12()) - prevfloat(1.0)
44+
end
3645

3746
#### BigFloat
3847

stdlib/Random/test/runtests.jl

+5-4
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ end
3333
@test length(randn(ComplexF64, 4, 5)) == 20
3434
@test length(bitrand(4, 5)) == 20
3535

36-
@test rand(MersenneTwister(0)) == 0.8236475079774124
37-
@test rand(MersenneTwister(42)) == 0.5331830160438613
36+
@test rand(MersenneTwister(0)) == 0.8236475079774125
37+
@test rand(MersenneTwister(42)) == 0.5331830160438614
3838
# Try a seed larger than 2^32
39-
@test rand(MersenneTwister(5294967296)) == 0.3498809918210497
39+
@test rand(MersenneTwister(5294967296)) == 0.3498809918210498
4040

4141
# Test array filling, Issues #7643, #8360
4242
@test rand(MersenneTwister(0), 1) == [0.8236475079774124]
@@ -307,7 +307,8 @@ let mt = MersenneTwister(0)
307307
Random.seed!(mt, 0)
308308
rand(mt) # this is to fill mt.vals, cf. #9040
309309
rand!(mt, A) # must not segfault even if Int(pointer(A)) % 16 != 0
310-
@test A[end-4:end] == [0.3371041633752143, 0.41147647589610803, 0.6063082992397912, 0.9103565379264364, 0.16456579813368521]
310+
@test A[end-4:end] == [0.3371041633752143, 0.41147647589610803, 0.6063082992397912,
311+
0.9103565379264366, 0.16456579813368533]
311312
end
312313
end
313314

0 commit comments

Comments
 (0)