forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdec2flt_table.py
executable file
·142 lines (118 loc) · 3.55 KB
/
dec2flt_table.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/env python3
"""
Generate powers of ten using William Clinger's ``AlgorithmM`` for use in
decimal to floating point conversions.
Specifically, computes and outputs (as Rust code) a table of 10^e for some
range of exponents e. The output is one array of 64 bit significands and
another array of corresponding base two exponents. The approximations are
normalized and rounded perfectly, i.e., within 0.5 ULP of the true value.
The representation ([u64], [i16]) instead of the more natural [(u64, i16)]
is used because (u64, i16) has a ton of padding which would make the table
even larger, and it's already uncomfortably large (6 KiB).
"""
from __future__ import print_function
from math import ceil, log
from fractions import Fraction
from collections import namedtuple
N = 64 # Size of the significand field in bits
MIN_SIG = 2 ** (N - 1)
MAX_SIG = (2 ** N) - 1
# Hand-rolled fp representation without arithmetic or any other operations.
# The significand is normalized and always N bit, but the exponent is
# unrestricted in range.
Fp = namedtuple('Fp', 'sig exp')
def algorithm_m(f, e):
assert f > 0
if e < 0:
u = f
v = 10 ** abs(e)
else:
u = f * 10 ** e
v = 1
k = 0
x = u // v
while True:
if x < MIN_SIG:
u <<= 1
k -= 1
elif x >= MAX_SIG:
v <<= 1
k += 1
else:
break
x = u // v
return ratio_to_float(u, v, k)
def ratio_to_float(u, v, k):
q, r = divmod(u, v)
v_r = v - r
z = Fp(q, k)
if r < v_r:
return z
elif r > v_r:
return next_float(z)
elif q % 2 == 0:
return z
else:
return next_float(z)
def next_float(z):
if z.sig == MAX_SIG:
return Fp(MIN_SIG, z.exp + 1)
else:
return Fp(z.sig + 1, z.exp)
def error(f, e, z):
decimal = f * Fraction(10) ** e
binary = z.sig * Fraction(2) ** z.exp
abs_err = abs(decimal - binary)
# The unit in the last place has value z.exp
ulp_err = abs_err / Fraction(2) ** z.exp
return float(ulp_err)
HEADER = """
//! Tables of approximations of powers of ten.
//! DO NOT MODIFY: Generated by `src/etc/dec2flt_table.py`
"""
def main():
print(HEADER.strip())
print()
print_proper_powers()
print()
print_short_powers(32, 24)
print()
print_short_powers(64, 53)
def print_proper_powers():
MIN_E = -305
MAX_E = 305
e_range = range(MIN_E, MAX_E+1)
powers = []
for e in e_range:
z = algorithm_m(1, e)
err = error(1, e, z)
assert err < 0.5
powers.append(z)
print("pub const MIN_E: i16 = {};".format(MIN_E))
print("pub const MAX_E: i16 = {};".format(MAX_E))
print()
print("#[rustfmt::skip]")
typ = "([u64; {0}], [i16; {0}])".format(len(powers))
print("pub const POWERS: ", typ, " = (", sep='')
print(" [")
for z in powers:
print(" 0x{:x},".format(z.sig))
print(" ],")
print(" [")
for z in powers:
print(" {},".format(z.exp))
print(" ],")
print(");")
def print_short_powers(num_bits, significand_size):
max_sig = 2**significand_size - 1
# The fast path bails out for exponents >= ceil(log5(max_sig))
max_e = int(ceil(log(max_sig, 5)))
e_range = range(max_e)
typ = "[f{}; {}]".format(num_bits, len(e_range))
print("#[rustfmt::skip]")
print("pub const F", num_bits, "_SHORT_POWERS: ", typ, " = [", sep='')
for e in e_range:
print(" 1e{},".format(e))
print("];")
if __name__ == '__main__':
main()