const std = @import("std"); /// A custom N-bit floating point type, representing `f * 2^e`. /// e is biased, so it be directly shifted into the exponent bits. /// Negative exponent indicates an invalid result. pub fn BiasedFp(comptime T: type) type { const MantissaT = mantissaType(T); return struct { const Self = @This(); /// The significant digits. f: MantissaT, /// The biased, binary exponent. e: i32, pub fn zero() Self { return .{ .f = 0, .e = 0 }; } pub fn zeroPow2(e: i32) Self { return .{ .f = 0, .e = e }; } pub fn inf(comptime FloatT: type) Self { return .{ .f = 0, .e = (1 << std.math.floatExponentBits(FloatT)) - 1 }; } pub fn eql(self: Self, other: Self) bool { return self.f == other.f and self.e == other.e; } pub fn toFloat(self: Self, comptime FloatT: type, negative: bool) FloatT { var word = self.f; word |= @as(MantissaT, @intCast(self.e)) << std.math.floatMantissaBits(FloatT); var f = floatFromUnsigned(FloatT, MantissaT, word); if (negative) f = -f; return f; } }; } pub fn floatFromUnsigned(comptime T: type, comptime MantissaT: type, v: MantissaT) T { return switch (T) { f16 => @as(f16, @bitCast(@as(u16, @truncate(v)))), f32 => @as(f32, @bitCast(@as(u32, @truncate(v)))), f64 => @as(f64, @bitCast(@as(u64, @truncate(v)))), f128 => @as(f128, @bitCast(v)), else => unreachable, }; } /// Represents a parsed floating point value as its components. pub fn Number(comptime T: type) type { return struct { exponent: i64, mantissa: mantissaType(T), negative: bool, /// More than max_mantissa digits were found during parse many_digits: bool, /// The number was a hex-float (e.g. 0x1.234p567) hex: bool, }; } /// Determine if 8 bytes are all decimal digits. /// This does not care about the order in which the bytes were loaded. pub fn isEightDigits(v: u64) bool { const a = v +% 0x4646_4646_4646_4646; const b = v -% 0x3030_3030_3030_3030; return ((a | b) & 0x8080_8080_8080_8080) == 0; } pub fn isDigit(c: u8, comptime base: u8) bool { std.debug.assert(base == 10 or base == 16); return if (base == 10) '0' <= c and c <= '9' else '0' <= c and c <= '9' or 'a' <= c and c <= 'f' or 'A' <= c and c <= 'F'; } /// Returns the underlying storage type used for the mantissa of floating-point type. /// The output unsigned type must have at least as many bits as the input floating-point type. pub fn mantissaType(comptime T: type) type { return switch (T) { f16, f32, f64 => u64, f128 => u128, else => unreachable, }; }