master
 1const std = @import("std");
 2
 3/// A custom N-bit floating point type, representing `f * 2^e`.
 4/// e is biased, so it be directly shifted into the exponent bits.
 5/// Negative exponent indicates an invalid result.
 6pub fn BiasedFp(comptime T: type) type {
 7    const MantissaT = mantissaType(T);
 8
 9    return struct {
10        const Self = @This();
11
12        /// The significant digits.
13        f: MantissaT,
14        /// The biased, binary exponent.
15        e: i32,
16
17        pub fn zero() Self {
18            return .{ .f = 0, .e = 0 };
19        }
20
21        pub fn zeroPow2(e: i32) Self {
22            return .{ .f = 0, .e = e };
23        }
24
25        pub fn inf(comptime FloatT: type) Self {
26            const e = (1 << std.math.floatExponentBits(FloatT)) - 1;
27            return switch (FloatT) {
28                f80 => .{ .f = 0x8000000000000000, .e = e },
29                else => .{ .f = 0, .e = e },
30            };
31        }
32
33        pub fn eql(self: Self, other: Self) bool {
34            return self.f == other.f and self.e == other.e;
35        }
36
37        pub fn toFloat(self: Self, comptime FloatT: type, negative: bool) FloatT {
38            var word = self.f;
39            word |= @as(MantissaT, @intCast(self.e)) << std.math.floatMantissaBits(FloatT);
40            var f = floatFromUnsigned(FloatT, MantissaT, word);
41            if (negative) f = -f;
42            return f;
43        }
44    };
45}
46
47pub fn floatFromUnsigned(comptime T: type, comptime MantissaT: type, v: MantissaT) T {
48    return switch (T) {
49        f16 => @as(f16, @bitCast(@as(u16, @truncate(v)))),
50        f32 => @as(f32, @bitCast(@as(u32, @truncate(v)))),
51        f64 => @as(f64, @bitCast(@as(u64, @truncate(v)))),
52        f80 => @as(f80, @bitCast(@as(u80, @truncate(v)))),
53        f128 => @as(f128, @bitCast(v)),
54        else => unreachable,
55    };
56}
57
58/// Represents a parsed floating point value as its components.
59pub fn Number(comptime T: type) type {
60    return struct {
61        exponent: i64,
62        mantissa: mantissaType(T),
63        negative: bool,
64        /// More than max_mantissa digits were found during parse
65        many_digits: bool,
66        /// The number was a hex-float (e.g. 0x1.234p567)
67        hex: bool,
68    };
69}
70
71/// Determine if 8 bytes are all decimal digits.
72/// This does not care about the order in which the bytes were loaded.
73pub fn isEightDigits(v: u64) bool {
74    const a = v +% 0x4646_4646_4646_4646;
75    const b = v -% 0x3030_3030_3030_3030;
76    return ((a | b) & 0x8080_8080_8080_8080) == 0;
77}
78
79pub fn isDigit(c: u8, comptime base: u8) bool {
80    std.debug.assert(base == 10 or base == 16);
81
82    return if (base == 10)
83        '0' <= c and c <= '9'
84    else
85        '0' <= c and c <= '9' or 'a' <= c and c <= 'f' or 'A' <= c and c <= 'F';
86}
87
88/// Returns the underlying storage type used for the mantissa of floating-point type.
89/// The output unsigned type must have at least as many bits as the input floating-point type.
90pub fn mantissaType(comptime T: type) type {
91    return switch (T) {
92        f16, f32, f64 => u64,
93        f80, f128 => u128,
94        else => unreachable,
95    };
96}