// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. module rand // NOTE: mini_math.v exists, so that we can avoid `import math`, // just for the math.log and math.sqrt functions needed for the // non uniform random number redistribution functions. // Importing math is relatively heavy, both in terms of compilation // speed (more source to process), and in terms of increases in the // generated executable sizes (if the rest of the program does not use // math already; many programs do not need math, for example the // compiler itself does not, while needing random number generation. const sqrt2 = 1.41421356237309504880168872420969807856967187537694807317667974 [inline] fn msqrt(a f64) f64 { if a == 0 { return a } mut x := a z, ex := frexp(x) w := x // approximate square root of number between 0.5 and 1 // relative error of approximation = 7.47e-3 x = 4.173075996388649989089e-1 + 5.9016206709064458299663e-1 * z // adjust for odd powers of 2 if (ex & 1) != 0 { x *= rand.sqrt2 } x = scalbn(x, ex >> 1) // newton iterations x = 0.5 * (x + w / x) x = 0.5 * (x + w / x) x = 0.5 * (x + w / x) return x } // a simplified approximation (without the edge cases), see math.log fn mlog(a f64) f64 { ln2_lo := 1.90821492927058770002e-10 ln2_hi := 0.693147180369123816490 l1 := 0.6666666666666735130 l2 := 0.3999999999940941908 l3 := 0.2857142874366239149 l4 := 0.2222219843214978396 l5 := 0.1818357216161805012 l6 := 0.1531383769920937332 l7 := 0.1479819860511658591 x := a mut f1, mut ki := frexp(x) if f1 < rand.sqrt2 / 2 { f1 *= 2 ki-- } f := f1 - 1 k := f64(ki) s := f / (2 + f) s2 := s * s s4 := s2 * s2 t1 := s2 * (l1 + s4 * (l3 + s4 * (l5 + s4 * l7))) t2 := s4 * (l2 + s4 * (l4 + s4 * l6)) r := t1 + t2 hfsq := 0.5 * f * f return k * ln2_hi - ((hfsq - (s * (hfsq + r) + k * ln2_lo)) - f) } fn frexp(x f64) (f64, int) { mut y := f64_bits(x) ee := int((y >> 52) & 0x7ff) if ee == 0 { if x != 0.0 { x1p64 := f64_from_bits(u64(0x43f0000000000000)) z, e_ := frexp(x * x1p64) return z, e_ - 64 } return x, 0 } else if ee == 0x7ff { return x, 0 } e_ := ee - 0x3fe y &= u64(0x800fffffffffffff) y |= u64(0x3fe0000000000000) return f64_from_bits(y), e_ } fn scalbn(x f64, n_ int) f64 { mut n := n_ x1p1023 := f64_from_bits(u64(0x7fe0000000000000)) x1p53 := f64_from_bits(u64(0x4340000000000000)) x1p_1022 := f64_from_bits(u64(0x0010000000000000)) mut y := x if n > 1023 { y *= x1p1023 n -= 1023 if n > 1023 { y *= x1p1023 n -= 1023 if n > 1023 { n = 1023 } } } else if n < -1022 { /* make sure final n < -53 to avoid double rounding in the subnormal range */ y *= x1p_1022 * x1p53 n += 1022 - 53 if n < -1022 { y *= x1p_1022 * x1p53 n += 1022 - 53 if n < -1022 { n = -1022 } } } return y * f64_from_bits(u64((0x3ff + n)) << 52) } [inline] fn f64_from_bits(b u64) f64 { return *unsafe { &f64(&b) } } [inline] fn f64_bits(f f64) u64 { return *unsafe { &u64(&f) } }