| 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 | |
| 11 | const std = @import("std"); |
| 12 | const math = std.math; |
| 13 | const common = @import("common.zig"); |
| 14 | const FloatInfo = @import("FloatInfo.zig"); |
| 15 | const Number = common.Number; |
| 16 | const floatFromU64 = common.floatFromU64; |
| 17 | |
| 18 | fn 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) |
| 31 | fn 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 | |
| 71 | fn 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 | |
| 105 | pub 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 | } |