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}