master
  1//! Representation of a float as the significant digits and exponent.
  2//! The fast path algorithm using machine-sized integers and floats.
  3//!
  4//! This only works if both the mantissa and the exponent can be exactly
  5//! represented as a machine float, since IEE-754 guarantees no rounding
  6//! will occur.
  7//!
  8//! There is an exception: disguised fast-path cases, where we can shift
  9//! powers-of-10 from the exponent to the significant digits.
 10
 11const std = @import("std");
 12const math = std.math;
 13const common = @import("common.zig");
 14const FloatInfo = @import("FloatInfo.zig");
 15const Number = common.Number;
 16const floatFromU64 = common.floatFromU64;
 17
 18fn isFastPath(comptime T: type, n: Number(T)) bool {
 19    const info = FloatInfo.from(T);
 20
 21    return info.min_exponent_fast_path <= n.exponent and
 22        n.exponent <= info.max_exponent_fast_path_disguised and
 23        n.mantissa <= info.max_mantissa_fast_path and
 24        !n.many_digits;
 25}
 26
 27// upper bound for tables is floor(mantissaDigits(T) / log2(5))
 28// for f64 this is floor(53 / log2(5)) = 22.
 29//
 30// Must have max_disguised_fast_path - max_exponent_fast_path entries. (82 - 48 = 34 for f128)
 31fn fastPow10(comptime T: type, i: usize) T {
 32    return switch (T) {
 33        f16 => ([8]f16{
 34            1e0, 1e1, 1e2, 1e3, 1e4, 0, 0, 0,
 35        })[i & 7],
 36
 37        f32 => ([16]f32{
 38            1e0, 1e1, 1e2,  1e3, 1e4, 1e5, 1e6, 1e7,
 39            1e8, 1e9, 1e10, 0,   0,   0,   0,   0,
 40        })[i & 15],
 41
 42        f64 => ([32]f64{
 43            1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
 44            1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
 45            1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 0,
 46            0,    0,    0,    0,    0,    0,    0,    0,
 47        })[i & 31],
 48
 49        f80 => ([32]f80{
 50            1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
 51            1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
 52            1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23,
 53            1e24, 1e25, 1e26, 1e27, 0,    0,    0,    0,
 54        })[i & 31],
 55
 56        f128 => ([64]f128{
 57            1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
 58            1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
 59            1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23,
 60            1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31,
 61            1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39,
 62            1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47,
 63            1e48, 0,    0,    0,    0,    0,    0,    0,
 64            0,    0,    0,    0,    0,    0,    0,    0,
 65        })[i & 63],
 66
 67        else => unreachable,
 68    };
 69}
 70
 71fn fastIntPow10(comptime T: type, i: usize) T {
 72    return switch (T) {
 73        u64 => ([16]u64{
 74            1,             10,             100,             1000,
 75            10000,         100000,         1000000,         10000000,
 76            100000000,     1000000000,     10000000000,     100000000000,
 77            1000000000000, 10000000000000, 100000000000000, 1000000000000000,
 78        })[i],
 79
 80        u128 => ([35]u128{
 81            1,                                   10,
 82            100,                                 1000,
 83            10000,                               100000,
 84            1000000,                             10000000,
 85            100000000,                           1000000000,
 86            10000000000,                         100000000000,
 87            1000000000000,                       10000000000000,
 88            100000000000000,                     1000000000000000,
 89            10000000000000000,                   100000000000000000,
 90            1000000000000000000,                 10000000000000000000,
 91            100000000000000000000,               1000000000000000000000,
 92            10000000000000000000000,             100000000000000000000000,
 93            1000000000000000000000000,           10000000000000000000000000,
 94            100000000000000000000000000,         1000000000000000000000000000,
 95            10000000000000000000000000000,       100000000000000000000000000000,
 96            1000000000000000000000000000000,     10000000000000000000000000000000,
 97            100000000000000000000000000000000,   1000000000000000000000000000000000,
 98            10000000000000000000000000000000000,
 99        })[i],
100
101        else => unreachable,
102    };
103}
104
105pub fn convertFast(comptime T: type, n: Number(T)) ?T {
106    const MantissaT = common.mantissaType(T);
107
108    if (!isFastPath(T, n)) {
109        return null;
110    }
111
112    // TODO: x86 (no SSE/SSE2) requires x87 FPU to be setup correctly with fldcw
113    const info = FloatInfo.from(T);
114
115    var value: T = 0;
116    if (n.exponent <= info.max_exponent_fast_path) {
117        // normal fast path
118        value = @as(T, @floatFromInt(n.mantissa));
119        value = if (n.exponent < 0)
120            value / fastPow10(T, @as(usize, @intCast(-n.exponent)))
121        else
122            value * fastPow10(T, @as(usize, @intCast(n.exponent)));
123    } else {
124        // disguised fast path
125        const shift = n.exponent - info.max_exponent_fast_path;
126        const mantissa = math.mul(MantissaT, n.mantissa, fastIntPow10(MantissaT, @as(usize, @intCast(shift)))) catch return null;
127        if (mantissa > info.max_mantissa_fast_path) {
128            return null;
129        }
130        value = @as(T, @floatFromInt(mantissa)) * fastPow10(T, info.max_exponent_fast_path);
131    }
132
133    if (n.negative) {
134        value = -value;
135    }
136    return value;
137}