|
| 1 | +#!/usr/bin/env python2.7 |
| 2 | +# |
| 3 | +# Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
| 4 | +# file at the top-level directory of this distribution and at |
| 5 | +# http://rust-lang.org/COPYRIGHT. |
| 6 | +# |
| 7 | +# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 8 | +# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 9 | +# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 10 | +# option. This file may not be copied, modified, or distributed |
| 11 | +# except according to those terms. |
| 12 | + |
| 13 | +""" |
| 14 | +Generate powers of ten using William Clinger's ``AlgorithmM`` for use in |
| 15 | +decimal to floating point conversions. |
| 16 | +
|
| 17 | +Specifically, computes and outputs (as Rust code) a table of 10^e for some |
| 18 | +range of exponents e. The output is one array of 64 bit significands and |
| 19 | +another array of corresponding base two exponents. The approximations are |
| 20 | +normalized and rounded perfectly, i.e., within 0.5 ULP of the true value. |
| 21 | +
|
| 22 | +The representation ([u64], [i16]) instead of the more natural [(u64, i16)] |
| 23 | +is used because (u64, i16) has a ton of padding which would make the table |
| 24 | +even larger, and it's already uncomfortably large (6 KiB). |
| 25 | +""" |
| 26 | +from __future__ import print_function |
| 27 | +import sys |
| 28 | +from fractions import Fraction |
| 29 | +from collections import namedtuple |
| 30 | + |
| 31 | + |
| 32 | +N = 64 # Size of the significand field in bits |
| 33 | +MIN_SIG = 2 ** (N - 1) |
| 34 | +MAX_SIG = (2 ** N) - 1 |
| 35 | + |
| 36 | + |
| 37 | +# Hand-rolled fp representation without arithmetic or any other operations. |
| 38 | +# The significand is normalized and always N bit, but the exponent is |
| 39 | +# unrestricted in range. |
| 40 | +Fp = namedtuple('Fp', 'sig exp') |
| 41 | + |
| 42 | + |
| 43 | +def algorithm_m(f, e): |
| 44 | + assert f > 0 |
| 45 | + if e < 0: |
| 46 | + u = f |
| 47 | + v = 10 ** abs(e) |
| 48 | + else: |
| 49 | + u = f * 10 ** e |
| 50 | + v = 1 |
| 51 | + k = 0 |
| 52 | + x = u // v |
| 53 | + while True: |
| 54 | + if x < MIN_SIG: |
| 55 | + u <<= 1 |
| 56 | + k -= 1 |
| 57 | + elif x >= MAX_SIG: |
| 58 | + v <<= 1 |
| 59 | + k += 1 |
| 60 | + else: |
| 61 | + break |
| 62 | + x = u // v |
| 63 | + return ratio_to_float(u, v, k) |
| 64 | + |
| 65 | + |
| 66 | +def ratio_to_float(u, v, k): |
| 67 | + q, r = divmod(u, v) |
| 68 | + v_r = v - r |
| 69 | + z = Fp(q, k) |
| 70 | + if r < v_r: |
| 71 | + return z |
| 72 | + elif r > v_r: |
| 73 | + return next_float(z) |
| 74 | + elif q % 2 == 0: |
| 75 | + return z |
| 76 | + else: |
| 77 | + return next_float(z) |
| 78 | + |
| 79 | + |
| 80 | +def next_float(z): |
| 81 | + if z.sig == MAX_SIG: |
| 82 | + return Fp(MIN_SIG, z.exp + 1) |
| 83 | + else: |
| 84 | + return Fp(z.sig + 1, z.exp) |
| 85 | + |
| 86 | + |
| 87 | +def error(f, e, z): |
| 88 | + decimal = f * Fraction(10) ** e |
| 89 | + binary = z.sig * Fraction(2) ** z.exp |
| 90 | + abs_err = abs(decimal - binary) |
| 91 | + # The unit in the last place has value z.exp |
| 92 | + ulp_err = abs_err / Fraction(2) ** z.exp |
| 93 | + return float(ulp_err) |
| 94 | + |
| 95 | +LICENSE = """ |
| 96 | +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
| 97 | +// file at the top-level directory of this distribution and at |
| 98 | +// http://rust-lang.org/COPYRIGHT. |
| 99 | +// |
| 100 | +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 101 | +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 102 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 103 | +// option. This file may not be copied, modified, or distributed |
| 104 | +// except according to those terms. |
| 105 | +""" |
| 106 | + |
| 107 | +def main(): |
| 108 | + MIN_E = -305 |
| 109 | + MAX_E = 305 |
| 110 | + e_range = range(MIN_E, MAX_E+1) |
| 111 | + powers = [] |
| 112 | + for e in e_range: |
| 113 | + z = algorithm_m(1, e) |
| 114 | + err = error(1, e, z) |
| 115 | + assert err < 0.5 |
| 116 | + powers.append(z) |
| 117 | + typ = "([u64; {0}], [i16; {0}])".format(len(e_range)) |
| 118 | + print(LICENSE.strip()) |
| 119 | + print("// Table of approximations of powers of ten.") |
| 120 | + print("// DO NOT MODIFY: Generated by a src/etc/dec2flt_table.py") |
| 121 | + print("pub const MIN_E: i16 = {};".format(MIN_E)) |
| 122 | + print("pub const MAX_E: i16 = {};".format(MAX_E)) |
| 123 | + print() |
| 124 | + print("pub const POWERS: ", typ, " = ([", sep='') |
| 125 | + for z in powers: |
| 126 | + print(" 0x{:x},".format(z.sig)) |
| 127 | + print("], [") |
| 128 | + for z in powers: |
| 129 | + print(" {},".format(z.exp)) |
| 130 | + print("]);") |
| 131 | + |
| 132 | + |
| 133 | +if __name__ == '__main__': |
| 134 | + main() |
0 commit comments