Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Script for generating the powers-of-ten tables necessary for correct and
fast decimal-to-float conversions.
- Loading branch information
Robin Kruppe
committed
Aug 8, 2015
1 parent
7ebd7f3
commit b7e39a1
Showing
1 changed file
with
134 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
#!/usr/bin/env python2.7 | ||
# | ||
# Copyright 2015 The Rust Project Developers. See the COPYRIGHT | ||
# file at the top-level directory of this distribution and at | ||
# http://rust-lang.org/COPYRIGHT. | ||
# | ||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
# option. This file may not be copied, modified, or distributed | ||
# except according to those terms. | ||
|
||
""" | ||
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 | ||
import sys | ||
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) | ||
|
||
LICENSE = """ | ||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
""" | ||
|
||
def main(): | ||
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) | ||
typ = "([u64; {0}], [i16; {0}])".format(len(e_range)) | ||
print(LICENSE.strip()) | ||
print("// Table of approximations of powers of ten.") | ||
print("// DO NOT MODIFY: Generated by a src/etc/dec2flt_table.py") | ||
print("pub const MIN_E: i16 = {};".format(MIN_E)) | ||
print("pub const MAX_E: i16 = {};".format(MAX_E)) | ||
print() | ||
print("pub const POWERS: ", typ, " = ([", sep='') | ||
for z in powers: | ||
print(" 0x{:x},".format(z.sig)) | ||
print("], [") | ||
for z in powers: | ||
print(" {},".format(z.exp)) | ||
print("]);") | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |