| 1 | const 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. |
| 6 | pub 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 | |
| 47 | pub 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. |
| 59 | pub 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. |
| 73 | pub 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 | |
| 79 | pub 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. |
| 90 | pub fn mantissaType(comptime T: type) type { |
| 91 | return switch (T) { |
| 92 | f16, f32, f64 => u64, |
| 93 | f80, f128 => u128, |
| 94 | else => unreachable, |
| 95 | }; |
| 96 | } |