v / vlib / rand / mini_math.v
130 lines · 121 sloc · 3.05 KB · 008aaad99981918c51194d7aaaaaccb4c258f244
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module rand
5
6// NOTE: mini_math.v exists, so that we can avoid `import math`,
7// just for the math.log and math.sqrt functions needed for the
8// non uniform random number redistribution functions.
9// Importing math is relatively heavy, both in terms of compilation
10// speed (more source to process), and in terms of increases in the
11// generated executable sizes (if the rest of the program does not use
12// math already; many programs do not need math, for example the
13// compiler itself does not, while needing random number generation.
14
15const sqrt2 = 1.41421356237309504880168872420969807856967187537694807317667974
16
17@[inline]
18fn msqrt(a f64) f64 {
19 if a == 0 {
20 return a
21 }
22 mut x := a
23 z, ex := frexp(x)
24 w := x
25 // approximate square root of number between 0.5 and 1
26 // relative error of approximation = 7.47e-3
27 x = 4.173075996388649989089e-1 + 5.9016206709064458299663e-1 * z // adjust for odd powers of 2
28 if (ex & 1) != 0 {
29 x *= sqrt2
30 }
31 x = scalbn(x, ex >> 1)
32 // newton iterations
33 x = 0.5 * (x + w / x)
34 x = 0.5 * (x + w / x)
35 x = 0.5 * (x + w / x)
36 return x
37}
38
39// a simplified approximation (without the edge cases), see math.log
40fn mlog(a f64) f64 {
41 ln2_lo := 1.90821492927058770002e-10
42 ln2_hi := 0.693147180369123816490
43 l1 := 0.6666666666666735130
44 l2 := 0.3999999999940941908
45 l3 := 0.2857142874366239149
46 l4 := 0.2222219843214978396
47 l5 := 0.1818357216161805012
48 l6 := 0.1531383769920937332
49 l7 := 0.1479819860511658591
50 x := a
51 mut f1, mut ki := frexp(x)
52 if f1 < sqrt2 / 2 {
53 f1 *= 2
54 ki--
55 }
56 f := f1 - 1
57 k := f64(ki)
58 s := f / (2 + f)
59 s2 := s * s
60 s4 := s2 * s2
61 t1 := s2 * (l1 + s4 * (l3 + s4 * (l5 + s4 * l7)))
62 t2 := s4 * (l2 + s4 * (l4 + s4 * l6))
63 r := t1 + t2
64 hfsq := 0.5 * f * f
65 return k * ln2_hi - ((hfsq - (s * (hfsq + r) + k * ln2_lo)) - f)
66}
67
68fn frexp(x f64) (f64, int) {
69 mut y := f64_bits(x)
70 ee := int((y >> 52) & 0x7ff)
71 if ee == 0 {
72 if x != 0.0 {
73 x1p64 := f64_from_bits(u64(0x43f0000000000000))
74 z, e_ := frexp(x * x1p64)
75 return z, e_ - 64
76 }
77 return x, 0
78 } else if ee == 0x7ff {
79 return x, 0
80 }
81 e_ := ee - 0x3fe
82 y &= u64(0x800fffffffffffff)
83 y |= u64(0x3fe0000000000000)
84 return f64_from_bits(y), e_
85}
86
87fn scalbn(x f64, n_ int) f64 {
88 mut n := n_
89 x1p1023 := f64_from_bits(u64(0x7fe0000000000000))
90 x1p53 := f64_from_bits(u64(0x4340000000000000))
91 x1p_1022 := f64_from_bits(u64(0x0010000000000000))
92
93 mut y := x
94 if n > 1023 {
95 y *= x1p1023
96 n -= 1023
97 if n > 1023 {
98 y *= x1p1023
99 n -= 1023
100 if n > 1023 {
101 n = 1023
102 }
103 }
104 } else if n < -1022 {
105 /*
106 make sure final n < -53 to avoid double
107 rounding in the subnormal range
108 */
109 y *= x1p_1022 * x1p53
110 n += 1022 - 53
111 if n < -1022 {
112 y *= x1p_1022 * x1p53
113 n += 1022 - 53
114 if n < -1022 {
115 n = -1022
116 }
117 }
118 }
119 return y * f64_from_bits(u64((0x3ff + n)) << 52)
120}
121
122@[inline]
123fn f64_from_bits(b u64) f64 {
124 return *unsafe { &f64(&b) }
125}
126
127@[inline]
128fn f64_bits(f f64) u64 {
129 return *unsafe { &u64(&f) }
130}
131