1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

rand: add full precision f32 and f64 random functions; fix f32/f64 multipliers (#16875)

This commit is contained in:
John
2023-01-19 09:21:47 -04:00
committed by GitHub
parent 550cae931f
commit 4098612a87
6 changed files with 337 additions and 15 deletions

View File

@ -191,16 +191,85 @@ pub fn (mut rng PRNG) i64_in_range(min i64, max i64) !i64 {
return min + rng.i64n(max - min)!
}
// f32 returns a pseudorandom `f32` value in range `[0, 1)`.
// f32 returns a pseudorandom `f32` value in range `[0, 1)`
// using rng.u32() multiplied by an f64 constant.
[inline]
pub fn (mut rng PRNG) f32() f32 {
return f32(rng.u32()) / constants.max_u32_as_f32
return f32((rng.u32() >> 9) * constants.reciprocal_2_23rd)
}
// f64 returns a pseudorandom `f64` value in range `[0, 1)`.
// f32cp returns a pseudorandom `f32` value in range `[0, 1)`
// with full precision (mantissa random between 0 and 1
// and the exponent varies as well.)
// See https://allendowney.com/research/rand/ for background on the method.
[inline]
pub fn (mut rng PRNG) f32cp() f32 {
mut x := rng.u32()
mut exp := u32(126)
mut mask := u32(1) << 31
// check if prng returns 0; rare but keep looking for precision
if _unlikely_(x == 0) {
x = rng.u32()
exp -= 31
}
// count leading one bits and scale exponent accordingly
for {
if x & mask != 0 {
mask >>= 1
exp -= 1
} else {
break
}
}
// if we used any high-order mantissa bits; replace x
if exp < (126 - 8) {
x = rng.u32()
}
// Assumes little-endian IEEE floating point.
x = (exp << 23) | (x >> 8) & constants.ieee754_mantissa_f32_mask
return bits.f32_from_bits(x)
}
// f64 returns a pseudorandom `f64` value in range `[0, 1)`
// using rng.u64() multiplied by a constant.
[inline]
pub fn (mut rng PRNG) f64() f64 {
return f64(rng.u64()) / constants.max_u64_as_f64
return f64((rng.u64() >> 12) * constants.reciprocal_2_52nd)
}
// f64cp returns a pseudorandom `f64` value in range `[0, 1)`
// with full precision (mantissa random between 0 and 1
// and the exponent varies as well.)
// See https://allendowney.com/research/rand/ for background on the method.
[inline]
pub fn (mut rng PRNG) f64cp() f64 {
mut x := rng.u64()
mut exp := u64(1022)
mut mask := u64(1) << 63
mut bitcount := u32(0)
// check if prng returns 0; unlikely.
if _unlikely_(x == 0) {
x = rng.u64()
exp -= 31
}
// count leading one bits and scale exponent accordingly
for {
if x & mask != 0 {
mask >>= 1
bitcount += 1
} else {
break
}
}
exp -= bitcount
if bitcount > 11 {
x = rng.u64()
}
x = (exp << 52) | (x & constants.ieee754_mantissa_f64_mask)
return bits.f64_from_bits(x)
}
// f32n returns a pseudorandom `f32` value in range `[0, max]`.
@ -520,11 +589,23 @@ pub fn f32() f32 {
return default_rng.f32()
}
// f32cp returns a uniformly distributed 32-bit floating point in range `[0, 1)`
// with full precision mantissa.
pub fn f32cp() f32 {
return default_rng.f32cp()
}
// f64 returns a uniformly distributed 64-bit floating point in range `[0, 1)`.
pub fn f64() f64 {
return default_rng.f64()
}
// f64 returns a uniformly distributed 64-bit floating point in range `[0, 1)`
// with full precision mantissa.
pub fn f64cp() f64 {
return default_rng.f64cp()
}
// f32n returns a uniformly distributed 32-bit floating point in range `[0, max)`.
pub fn f32n(max f32) !f32 {
return default_rng.f32n(max)