motiejus/zig

fork of https://codeberg.org/ziglang/zig
git clone https://git.jakstys.lt/motiejus/zig.git
Log | Tree | Refs | README | LICENSE

commit 31791ae15bfa8590cb235ce9d7c87a97d5136eef (tree)
parent afdc41cfd61db23f545d9239b61695e2ae991713
Author: Marc Tiehuis <marc@tiehu.is>
Date:   Thu, 21 Mar 2024 13:57:57 +1300

rename ryu128 -> format_float

Symmetry with parse_float and to hide the implementation from the user.
Additionally, we expose the entire namespace and provide some aliases so
everything is available to a user.

Closes #19366

Diffstat:
MCMakeLists.txt | 2+-
Mlib/std/fmt.zig | 17++++++++++-------
Alib/std/fmt/format_float.zig | 1139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dlib/std/fmt/ryu128.zig | 1139-------------------------------------------------------------------------------
4 files changed, 1150 insertions(+), 1147 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -235,7 +235,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/elf.zig" "${CMAKE_SOURCE_DIR}/lib/std/fifo.zig" "${CMAKE_SOURCE_DIR}/lib/std/fmt.zig" - "${CMAKE_SOURCE_DIR}/lib/std/fmt/ryu128.zig" + "${CMAKE_SOURCE_DIR}/lib/std/fmt/format_float.zig" "${CMAKE_SOURCE_DIR}/lib/std/fmt/parse_float.zig" "${CMAKE_SOURCE_DIR}/lib/std/fs.zig" "${CMAKE_SOURCE_DIR}/lib/std/fs/AtomicFile.zig" diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig @@ -9,7 +9,6 @@ const assert = std.debug.assert; const mem = std.mem; const unicode = std.unicode; const meta = std.meta; -const ryu128 = @import("fmt/ryu128.zig"); const lossyCast = std.math.lossyCast; const expectFmt = std.testing.expectFmt; @@ -757,21 +756,25 @@ pub fn formatIntValue( return formatInt(int_value, base, case, options, writer); } +pub const format_float = @import("fmt/format_float.zig"); +pub const formatFloat = format_float.formatFloat; +pub const FormatFloatError = format_float.FormatError; + fn formatFloatValue( value: anytype, comptime fmt: []const u8, options: FormatOptions, writer: anytype, ) !void { - var buf: [ryu128.bufferSize(.decimal, f64)]u8 = undefined; + var buf: [format_float.bufferSize(.decimal, f64)]u8 = undefined; if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) { - const s = ryu128.format(&buf, value, .{ .mode = .scientific, .precision = options.precision }) catch |err| switch (err) { + const s = formatFloat(&buf, value, .{ .mode = .scientific, .precision = options.precision }) catch |err| switch (err) { error.BufferTooSmall => "(float)", }; return formatBuf(s, options, writer); } else if (comptime std.mem.eql(u8, fmt, "d")) { - const s = ryu128.format(&buf, value, .{ .mode = .decimal, .precision = options.precision }) catch |err| switch (err) { + const s = formatFloat(&buf, value, .{ .mode = .decimal, .precision = options.precision }) catch |err| switch (err) { error.BufferTooSmall => "(float)", }; return formatBuf(s, options, writer); @@ -787,7 +790,7 @@ fn formatFloatValue( } test { - _ = &ryu128; + _ = &format_float; } pub const Case = enum { lower, upper }; @@ -890,7 +893,7 @@ fn formatSizeImpl(comptime base: comptime_int) type { return formatBuf("0B", options, writer); } // The worst case in terms of space needed is 32 bytes + 3 for the suffix. - var buf: [ryu128.min_buffer_size + 3]u8 = undefined; + var buf: [format_float.min_buffer_size + 3]u8 = undefined; const mags_si = " kMGTPEZY"; const mags_iec = " KMGTPEZY"; @@ -908,7 +911,7 @@ fn formatSizeImpl(comptime base: comptime_int) type { else => unreachable, }; - const s = ryu128.format(&buf, new_value, .{ .mode = .decimal, .precision = options.precision }) catch |err| switch (err) { + const s = formatFloat(&buf, new_value, .{ .mode = .decimal, .precision = options.precision }) catch |err| switch (err) { error.BufferTooSmall => unreachable, }; diff --git a/lib/std/fmt/format_float.zig b/lib/std/fmt/format_float.zig @@ -0,0 +1,1139 @@ +//! This file implements the ryu floating point conversion algorithm: +//! https://dl.acm.org/doi/pdf/10.1145/3360595 + +const std = @import("std"); +const expectFmt = std.testing.expectFmt; + +const special_exponent = 0x7fffffff; + +/// Any buffer used for `format` must be at least this large. This is asserted. A runtime check will +/// additionally be performed if more bytes are required. +pub const min_buffer_size = 53; + +/// Returns the minimum buffer size needed to print every float of a specific type and format. +pub fn bufferSize(comptime mode: Format, comptime T: type) comptime_int { + comptime std.debug.assert(@typeInfo(T) == .Float); + return switch (mode) { + .scientific => 53, + // Based on minimum subnormal values. + .decimal => switch (@bitSizeOf(T)) { + 16 => @max(15, min_buffer_size), + 32 => 55, + 64 => 347, + 80 => 4996, + 128 => 5011, + else => unreachable, + }, + }; +} + +pub const FormatError = error{ + BufferTooSmall, +}; + +pub const Format = enum { + scientific, + decimal, +}; + +pub const FormatOptions = struct { + mode: Format = .scientific, + precision: ?usize = null, +}; + +/// Format a floating-point value and write it to buffer. Returns a slice to the buffer containing +/// the string representation. +/// +/// Full precision is the default. Any full precision float can be reparsed with std.fmt.parseFloat +/// unambiguously. +/// +/// Scientific mode is recommended generally as the output is more compact and any type can be +/// written in full precision using a buffer of only `min_buffer_size`. +/// +/// When printing full precision decimals, use `bufferSize` to get the required space. It is +/// recommended to bound decimal output with a fixed precision to reduce the required buffer size. +pub fn formatFloat(buf: []u8, v_: anytype, options: FormatOptions) FormatError![]const u8 { + const v = switch (@TypeOf(v_)) { + // comptime_float internally is a f128; this preserves precision. + comptime_float => @as(f128, v_), + else => v_, + }; + + const T = @TypeOf(v); + comptime std.debug.assert(@typeInfo(T) == .Float); + const I = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } }); + + const has_explicit_leading_bit = std.math.floatMantissaBits(T) - std.math.floatFractionalBits(T) != 0; + const d = binaryToDecimal(@as(I, @bitCast(v)), std.math.floatMantissaBits(T), std.math.floatExponentBits(T), has_explicit_leading_bit); + + return switch (options.mode) { + .scientific => formatScientific(buf, d, options.precision), + .decimal => formatDecimal(buf, d, options.precision), + }; +} + +pub const FloatDecimal128 = struct { + mantissa: u128, + exponent: i32, + sign: bool, +}; + +fn copySpecialStr(buf: []u8, f: FloatDecimal128) []const u8 { + if (f.sign) { + buf[0] = '-'; + } + const offset: usize = @intFromBool(f.sign); + if (f.mantissa != 0) { + @memcpy(buf[offset..][0..3], "nan"); + return buf[0 .. 3 + offset]; + } + @memcpy(buf[offset..][0..3], "inf"); + return buf[0 .. 3 + offset]; +} + +fn writeDecimal(buf: []u8, value: anytype, count: usize) void { + var i: usize = 0; + + while (i + 2 < count) : (i += 2) { + const c: u8 = @intCast(value.* % 100); + value.* /= 100; + const d = std.fmt.digits2(c); + buf[count - i - 1] = d[1]; + buf[count - i - 2] = d[0]; + } + + while (i < count) : (i += 1) { + const c: u8 = @intCast(value.* % 10); + value.* /= 10; + buf[count - i - 1] = '0' + c; + } +} + +fn isPowerOf10(n_: u128) bool { + var n = n_; + while (n != 0) : (n /= 10) { + if (n % 10 != 0) return false; + } + return true; +} + +const RoundMode = enum { + /// 1234.56 = precision 2 + decimal, + /// 1.23456e3 = precision 5 + scientific, +}; + +fn round(f: FloatDecimal128, mode: RoundMode, precision: usize) FloatDecimal128 { + var round_digit: usize = 0; + var output = f.mantissa; + var exp = f.exponent; + const olength = decimalLength(output); + + switch (mode) { + .decimal => { + if (f.exponent > 0) { + round_digit = (olength - 1) + precision + @as(usize, @intCast(f.exponent)); + } else { + const min_exp_required = @as(usize, @intCast(-f.exponent)); + if (precision + olength > min_exp_required) { + round_digit = precision + olength - min_exp_required; + } + } + }, + .scientific => { + round_digit = 1 + precision; + }, + } + + if (round_digit < olength) { + var nlength = olength; + for (round_digit + 1..olength) |_| { + output /= 10; + exp += 1; + nlength -= 1; + } + + if (output % 10 >= 5) { + output /= 10; + output += 1; + exp += 1; + + // e.g. 9999 -> 10000 + if (isPowerOf10(output)) { + output /= 10; + exp += 1; + } + } + } + + return .{ + .mantissa = output, + .exponent = exp, + .sign = f.sign, + }; +} + +/// Write a FloatDecimal128 to a buffer in scientific form. +/// +/// The buffer provided must be greater than `min_buffer_size` in length. If no precision is +/// specified, this function will never return an error. If a precision is specified, up to +/// `8 + precision` bytes will be written to the buffer. An error will be returned if the content +/// will not fit. +/// +/// It is recommended to bound decimal formatting with an exact precision. +pub fn formatScientific(buf: []u8, f_: FloatDecimal128, precision: ?usize) FormatError![]const u8 { + std.debug.assert(buf.len >= min_buffer_size); + var f = f_; + + if (f.exponent == special_exponent) { + return copySpecialStr(buf, f); + } + + if (precision) |prec| { + f = round(f, .scientific, prec); + } + + var output = f.mantissa; + const olength = decimalLength(output); + + if (precision) |prec| { + // fixed bound: sign(1) + leading_digit(1) + point(1) + exp_sign(1) + exp_max(4) + const req_bytes = 8 + prec; + if (buf.len < req_bytes) { + return error.BufferTooSmall; + } + } + + // Step 5: Print the scientific representation + var index: usize = 0; + if (f.sign) { + buf[index] = '-'; + index += 1; + } + + // 1.12345 + writeDecimal(buf[index + 2 ..], &output, olength - 1); + buf[index] = '0' + @as(u8, @intCast(output % 10)); + buf[index + 1] = '.'; + index += 2; + const dp_index = index; + if (olength > 1) index += olength - 1 else index -= 1; + + if (precision) |prec| { + index += @intFromBool(olength == 1); + if (prec > olength - 1) { + const len = prec - (olength - 1); + @memset(buf[index..][0..len], '0'); + index += len; + } else { + index = dp_index + prec - @intFromBool(prec == 0); + } + } + + // e100 + buf[index] = 'e'; + index += 1; + var exp = f.exponent + @as(i32, @intCast(olength)) - 1; + if (exp < 0) { + buf[index] = '-'; + index += 1; + exp = -exp; + } + var uexp: u32 = @intCast(exp); + const elength = decimalLength(uexp); + writeDecimal(buf[index..], &uexp, elength); + index += elength; + + return buf[0..index]; +} + +/// Write a FloatDecimal128 to a buffer in decimal form. +/// +/// The buffer provided must be greater than `min_buffer_size` bytes in length. If no precision is +/// specified, this may still return an error. If precision is specified, `2 + precision` bytes will +/// always be written. +pub fn formatDecimal(buf: []u8, f_: FloatDecimal128, precision: ?usize) FormatError![]const u8 { + std.debug.assert(buf.len >= min_buffer_size); + var f = f_; + + if (f.exponent == special_exponent) { + return copySpecialStr(buf, f); + } + + if (precision) |prec| { + f = round(f, .decimal, prec); + } + + var output = f.mantissa; + const olength = decimalLength(output); + + // fixed bound: leading_digit(1) + point(1) + const req_bytes = if (f.exponent >= 0) + 2 + @abs(f.exponent) + olength + (precision orelse 0) + else + 2 + @max(@abs(f.exponent) + olength, precision orelse 0); + if (buf.len < req_bytes) { + return error.BufferTooSmall; + } + + // Step 5: Print the decimal representation + var index: usize = 0; + if (f.sign) { + buf[index] = '-'; + index += 1; + } + + const dp_offset = f.exponent + cast_i32(olength); + if (dp_offset <= 0) { + // 0.000001234 + buf[index] = '0'; + buf[index + 1] = '.'; + index += 2; + const dp_index = index; + + const dp_poffset: u32 = @intCast(-dp_offset); + @memset(buf[index..][0..dp_poffset], '0'); + index += dp_poffset; + writeDecimal(buf[index..], &output, olength); + index += olength; + + if (precision) |prec| { + const dp_written = index - dp_index; + if (prec > dp_written) { + @memset(buf[index..][0 .. prec - dp_written], '0'); + } + index = dp_index + prec - @intFromBool(prec == 0); + } + } else { + // 123456000 + const dp_uoffset: usize = @intCast(dp_offset); + if (dp_uoffset >= olength) { + writeDecimal(buf[index..], &output, olength); + index += olength; + @memset(buf[index..][0 .. dp_uoffset - olength], '0'); + index += dp_uoffset - olength; + + if (precision) |prec| { + if (prec != 0) { + buf[index] = '.'; + index += 1; + @memset(buf[index..][0..prec], '0'); + index += prec; + } + } + } else { + // 12345.6789 + writeDecimal(buf[index + dp_uoffset + 1 ..], &output, olength - dp_uoffset); + buf[index + dp_uoffset] = '.'; + const dp_index = index + dp_uoffset + 1; + writeDecimal(buf[index..], &output, dp_uoffset); + index += olength + 1; + + if (precision) |prec| { + const dp_written = olength - dp_uoffset; + if (prec > dp_written) { + @memset(buf[index..][0 .. prec - dp_written], '0'); + } + index = dp_index + prec - @intFromBool(prec == 0); + } + } + } + + return buf[0..index]; +} + +fn cast_i32(v: anytype) i32 { + return @intCast(v); +} + +/// Convert a binary float representation to decimal. +pub fn binaryToDecimal(bits: u128, mantissa_bits: u7, exponent_bits: u5, explicit_leading_bit: bool) FloatDecimal128 { + const bias = (@as(u32, 1) << (exponent_bits - 1)) - 1; + const ieee_sign = ((bits >> (mantissa_bits + exponent_bits)) & 1) != 0; + const ieee_mantissa = bits & ((@as(u128, 1) << mantissa_bits) - 1); + const ieee_exponent: u32 = @intCast((bits >> mantissa_bits) & ((@as(u128, 1) << exponent_bits) - 1)); + + if (ieee_exponent == 0 and ieee_mantissa == 0) { + return .{ + .mantissa = 0, + .exponent = 0, + .sign = ieee_sign, + }; + } + if (ieee_exponent == ((@as(u32, 1) << exponent_bits) - 1)) { + return .{ + .mantissa = if (explicit_leading_bit) ieee_mantissa & ((@as(u128, 1) << (mantissa_bits - 1)) - 1) else ieee_mantissa, + .exponent = 0x7fffffff, + .sign = ieee_sign, + }; + } + + var e2: i32 = undefined; + var m2: u128 = undefined; + if (explicit_leading_bit) { + if (ieee_exponent == 0) { + e2 = 1 - cast_i32(bias) - cast_i32(mantissa_bits) + 1 - 2; + } else { + e2 = cast_i32(ieee_exponent) - cast_i32(bias) - cast_i32(mantissa_bits) + 1 - 2; + } + m2 = ieee_mantissa; + } else { + if (ieee_exponent == 0) { + e2 = 1 - cast_i32(bias) - cast_i32(mantissa_bits) - 2; + m2 = ieee_mantissa; + } else { + e2 = cast_i32(ieee_exponent) - cast_i32(bias) - cast_i32(mantissa_bits) - 2; + m2 = (@as(u128, 1) << mantissa_bits) | ieee_mantissa; + } + } + const even = (m2 & 1) == 0; + const accept_bounds = even; + + // Step 2: Determine the interval of legal decimal representations. + const mv = 4 * m2; + const mm_shift: u1 = @intFromBool((ieee_mantissa != if (explicit_leading_bit) (@as(u128, 1) << (mantissa_bits - 1)) else 0) or (ieee_exponent == 0)); + + // Step 3: Convert to a decimal power base using 128-bit arithmetic. + var vr: u128 = undefined; + var vp: u128 = undefined; + var vm: u128 = undefined; + var e10: i32 = undefined; + var vm_is_trailing_zeros = false; + var vr_is_trailing_zeros = false; + if (e2 >= 0) { + const q: u32 = log10Pow2(@intCast(e2)) - @intFromBool(e2 > 3); + e10 = cast_i32(q); + const k: i32 = @intCast(FLOAT_128_POW5_INV_BITCOUNT + pow5Bits(q) - 1); + const i: u32 = @intCast(-e2 + cast_i32(q) + k); + + const pow5 = computeInvPow5(q); + vr = mulShift(4 * m2, &pow5, i); + vp = mulShift(4 * m2 + 2, &pow5, i); + vm = mulShift(4 * m2 - 1 - mm_shift, &pow5, i); + + if (q <= 55) { + if (mv % 5 == 0) { + vr_is_trailing_zeros = multipleOfPowerOf5(mv, q -% 1); + } else if (accept_bounds) { + vm_is_trailing_zeros = multipleOfPowerOf5(mv - 1 - mm_shift, q); + } else { + vp -= @intFromBool(multipleOfPowerOf5(mv + 2, q)); + } + } + } else { + const q: u32 = log10Pow5(@intCast(-e2)) - @intFromBool(-e2 > 1); + e10 = cast_i32(q) + e2; + const i: i32 = -e2 - cast_i32(q); + const k: i32 = cast_i32(pow5Bits(@intCast(i))) - FLOAT_128_POW5_BITCOUNT; + const j: u32 = @intCast(cast_i32(q) - k); + + const pow5 = computePow5(@intCast(i)); + vr = mulShift(4 * m2, &pow5, j); + vp = mulShift(4 * m2 + 2, &pow5, j); + vm = mulShift(4 * m2 - 1 - mm_shift, &pow5, j); + + if (q <= 1) { + vr_is_trailing_zeros = true; + if (accept_bounds) { + vm_is_trailing_zeros = mm_shift == 1; + } else { + vp -= 1; + } + } else if (q < 127) { + vr_is_trailing_zeros = multipleOfPowerOf2(mv, q - 1); + } + } + + // Step 4: Find the shortest decimal representation in the interval of legal representations. + var removed: u32 = 0; + var last_removed_digit: u8 = 0; + + while (vp / 10 > vm / 10) { + vm_is_trailing_zeros = vm_is_trailing_zeros and vm % 10 == 0; + vr_is_trailing_zeros = vr_is_trailing_zeros and last_removed_digit == 0; + last_removed_digit = @intCast(vr % 10); + vr /= 10; + vp /= 10; + vm /= 10; + removed += 1; + } + + if (vm_is_trailing_zeros) { + while (vm % 10 == 0) { + vr_is_trailing_zeros = vr_is_trailing_zeros and last_removed_digit == 0; + last_removed_digit = @intCast(vr % 10); + vr /= 10; + vp /= 10; + vm /= 10; + removed += 1; + } + } + + if (vr_is_trailing_zeros and (last_removed_digit == 5) and (vr % 2 == 0)) { + last_removed_digit = 4; + } + + return .{ + .mantissa = vr + @intFromBool((vr == vm and (!accept_bounds or !vm_is_trailing_zeros)) or last_removed_digit >= 5), + .exponent = e10 + cast_i32(removed), + .sign = ieee_sign, + }; +} + +fn decimalLength(v: u128) u32 { + const LARGEST_POW10 = (@as(u128, 5421010862427522170) << 64) | 687399551400673280; + var p10 = LARGEST_POW10; + var i: u32 = 39; + while (i > 0) : (i -= 1) { + if (v >= p10) return i; + p10 /= 10; + } + return 1; +} + +// floor(log_10(2^e)) +fn log10Pow2(e: u32) u32 { + std.debug.assert(e <= 1 << 15); + return @intCast((@as(u64, @intCast(e)) * 169464822037455) >> 49); +} + +// floor(log_10(5^e)) +fn log10Pow5(e: u32) u32 { + std.debug.assert(e <= 1 << 15); + return @intCast((@as(u64, @intCast(e)) * 196742565691928) >> 48); +} + +// if (e == 0) 1 else ceil(log_2(5^e)) +fn pow5Bits(e: u32) u32 { + std.debug.assert(e <= 1 << 15); + return @intCast(((@as(u64, @intCast(e)) * 163391164108059) >> 46) + 1); +} + +fn pow5Factor(value_: u128) u32 { + var count: u32 = 0; + var value = value_; + while (value > 0) : ({ + count += 1; + value /= 5; + }) { + if (value % 5 != 0) return count; + } + return 0; +} + +fn multipleOfPowerOf5(value: u128, p: u32) bool { + return pow5Factor(value) >= p; +} + +fn multipleOfPowerOf2(value: u128, p: u32) bool { + return (value & ((@as(u128, 1) << @as(u7, @intCast(p))) - 1)) == 0; +} + +fn computeInvPow5(i: u32) [4]u64 { + const base = (i + POW5_TABLE_SIZE - 1) / POW5_TABLE_SIZE; + const base2 = base * POW5_TABLE_SIZE; + const mul = &GENERIC_POW5_INV_SPLIT[base]; // 1 / 5^base2 + if (i == base2) { + return .{ mul[0] + 1, mul[1], mul[2], mul[3] }; + } else { + const offset = base2 - i; + const m = &GENERIC_POW5_TABLE[offset]; // 5^offset + const delta = pow5Bits(base2) - pow5Bits(i); + + const shift: u6 = @intCast(2 * (i % 32)); + const corr: u32 = @intCast(((POW5_INV_ERRORS[i / 32] >> shift) & 3) + 1); + return mul_128_256_shift(m, mul, delta, corr); + } +} + +fn computePow5(i: u32) [4]u64 { + const base = i / POW5_TABLE_SIZE; + const base2 = base * POW5_TABLE_SIZE; + const mul = &GENERIC_POW5_SPLIT[base]; + if (i == base2) { + return mul.*; + } else { + const offset = i - base2; + const m = &GENERIC_POW5_TABLE[offset]; + const delta = pow5Bits(i) - pow5Bits(base2); + + const shift: u6 = @intCast(2 * (i % 32)); + const corr: u32 = @intCast((POW5_ERRORS[i / 32] >> shift) & 3); + return mul_128_256_shift(m, mul, delta, corr); + } +} + +fn mulShift(m: u128, mul: *const [4]u64, j: u32) u128 { + std.debug.assert(j > 128); + const a: [2]u64 = .{ @truncate(m), @truncate(m >> 64) }; + const r = mul_128_256_shift(&a, mul, j, 0); + return (@as(u128, r[1]) << 64) | r[0]; +} + +fn mul_128_256_shift(a: *const [2]u64, b: *const [4]u64, shift: u32, corr: u32) [4]u64 { + std.debug.assert(shift > 0); + std.debug.assert(shift < 256); + + const b00 = @as(u128, a[0]) * b[0]; + const b01 = @as(u128, a[0]) * b[1]; + const b02 = @as(u128, a[0]) * b[2]; + const b03 = @as(u128, a[0]) * b[3]; + const b10 = @as(u128, a[1]) * b[0]; + const b11 = @as(u128, a[1]) * b[1]; + const b12 = @as(u128, a[1]) * b[2]; + const b13 = @as(u128, a[1]) * b[3]; + + const s0 = b00; + const s1 = b01 +% b10; + const c1: u128 = @intFromBool(s1 < b01); + const s2 = b02 +% b11; + const c2: u128 = @intFromBool(s2 < b02); + const s3 = b03 +% b12; + const c3: u128 = @intFromBool(s3 < b03); + + const p0 = s0 +% (s1 << 64); + const d0: u128 = @intFromBool(p0 < b00); + const q1 = s2 +% (s1 >> 64) +% (s3 << 64); + const d1: u128 = @intFromBool(q1 < s2); + const p1 = q1 +% (c1 << 64) +% d0; + const d2: u128 = @intFromBool(p1 < q1); + const p2 = b13 +% (s3 >> 64) +% c2 +% (c3 << 64) +% d1 +% d2; + + var r0: u128 = undefined; + var r1: u128 = undefined; + if (shift < 128) { + const cshift: u7 = @intCast(shift); + const sshift: u7 = @intCast(128 - shift); + r0 = corr +% ((p0 >> cshift) | (p1 << sshift)); + r1 = ((p1 >> cshift) | (p2 << sshift)) +% @intFromBool(r0 < corr); + } else if (shift == 128) { + r0 = corr +% p1; + r1 = p2 +% @intFromBool(r0 < corr); + } else { + const ashift: u7 = @intCast(shift - 128); + const sshift: u7 = @intCast(256 - shift); + r0 = corr +% ((p1 >> ashift) | (p2 << sshift)); + r1 = (p2 >> ashift) +% @intFromBool(r0 < corr); + } + + return .{ @truncate(r0), @truncate(r0 >> 64), @truncate(r1), @truncate(r1 >> 64) }; +} + +// zig fmt: off +// +// 4.5KiB of tables. + +const FLOAT_128_POW5_INV_BITCOUNT = 249; +const FLOAT_128_POW5_BITCOUNT = 249; +const POW5_TABLE_SIZE = 56; + +const GENERIC_POW5_TABLE: [POW5_TABLE_SIZE][2]u64 = .{ + .{ 1, 0 }, + .{ 5, 0 }, + .{ 25, 0 }, + .{ 125, 0 }, + .{ 625, 0 }, + .{ 3125, 0 }, + .{ 15625, 0 }, + .{ 78125, 0 }, + .{ 390625, 0 }, + .{ 1953125, 0 }, + .{ 9765625, 0 }, + .{ 48828125, 0 }, + .{ 244140625, 0 }, + .{ 1220703125, 0 }, + .{ 6103515625, 0 }, + .{ 30517578125, 0 }, + .{ 152587890625, 0 }, + .{ 762939453125, 0 }, + .{ 3814697265625, 0 }, + .{ 19073486328125, 0 }, + .{ 95367431640625, 0 }, + .{ 476837158203125, 0 }, + .{ 2384185791015625, 0 }, + .{ 11920928955078125, 0 }, + .{ 59604644775390625, 0 }, + .{ 298023223876953125, 0 }, + .{ 1490116119384765625, 0 }, + .{ 7450580596923828125, 0 }, + .{ 359414837200037393, 2 }, + .{ 1797074186000186965, 10 }, + .{ 8985370930000934825, 50 }, + .{ 8033366502585570893, 252 }, + .{ 3273344365508751233, 1262 }, + .{ 16366721827543756165, 6310 }, + .{ 8046632842880574361, 31554 }, + .{ 3339676066983768573, 157772 }, + .{ 16698380334918842865, 788860 }, + .{ 9704925379756007861, 3944304 }, + .{ 11631138751360936073, 19721522 }, + .{ 2815461535676025517, 98607613 }, + .{ 14077307678380127585, 493038065 }, + .{ 15046306170771983077, 2465190328 }, + .{ 1444554559021708921, 12325951644 }, + .{ 7222772795108544605, 61629758220 }, + .{ 17667119901833171409, 308148791101 }, + .{ 14548623214327650581, 1540743955509 }, + .{ 17402883850509598057, 7703719777548 }, + .{ 13227442957709783821, 38518598887744 }, + .{ 10796982567420264257, 192592994438723 }, + .{ 17091424689682218053, 962964972193617 }, + .{ 11670147153572883801, 4814824860968089 }, + .{ 3010503546735764157, 24074124304840448 }, + .{ 15052517733678820785, 120370621524202240 }, + .{ 1475612373555897461, 601853107621011204 }, + .{ 7378061867779487305, 3009265538105056020 }, + .{ 18443565265187884909, 15046327690525280101 }, +}; + +const GENERIC_POW5_SPLIT: [89][4]u64 = .{ + .{ 0, 0, 0, 72057594037927936 }, + .{ 0, 5206161169240293376, 4575641699882439235, 73468396926392969 }, + .{ 3360510775605221349, 6983200512169538081, 4325643253124434363, 74906821675075173 }, + .{ 11917660854915489451, 9652941469841108803, 946308467778435600, 76373409087490117 }, + .{ 1994853395185689235, 16102657350889591545, 6847013871814915412, 77868710555449746 }, + .{ 958415760277438274, 15059347134713823592, 7329070255463483331, 79393288266368765 }, + .{ 2065144883315240188, 7145278325844925976, 14718454754511147343, 80947715414629833 }, + .{ 8980391188862868935, 13709057401304208685, 8230434828742694591, 82532576417087045 }, + .{ 432148644612782575, 7960151582448466064, 12056089168559840552, 84148467132788711 }, + .{ 484109300864744403, 15010663910730448582, 16824949663447227068, 85795995087002057 }, + .{ 14793711725276144220, 16494403799991899904, 10145107106505865967, 87475779699624060 }, + .{ 15427548291869817042, 12330588654550505203, 13980791795114552342, 89188452518064298 }, + .{ 9979404135116626552, 13477446383271537499, 14459862802511591337, 90934657454687378 }, + .{ 12385121150303452775, 9097130814231585614, 6523855782339765207, 92715051028904201 }, + .{ 1822931022538209743, 16062974719797586441, 3619180286173516788, 94530302614003091 }, + .{ 12318611738248470829, 13330752208259324507, 10986694768744162601, 96381094688813589 }, + .{ 13684493829640282333, 7674802078297225834, 15208116197624593182, 98268123094297527 }, + .{ 5408877057066295332, 6470124174091971006, 15112713923117703147, 100192097295163851 }, + .{ 11407083166564425062, 18189998238742408185, 4337638702446708282, 102153740646605557 }, + .{ 4112405898036935485, 924624216579956435, 14251108172073737125, 104153790666259019 }, + .{ 16996739107011444789, 10015944118339042475, 2395188869672266257, 106192999311487969 }, + .{ 4588314690421337879, 5339991768263654604, 15441007590670620066, 108272133262096356 }, + .{ 2286159977890359825, 14329706763185060248, 5980012964059367667, 110391974208576409 }, + .{ 9654767503237031099, 11293544302844823188, 11739932712678287805, 112553319146000238 }, + .{ 11362964448496095896, 7990659682315657680, 251480263940996374, 114756980673665505 }, + .{ 1423410421096377129, 14274395557581462179, 16553482793602208894, 117003787300607788 }, + .{ 2070444190619093137, 11517140404712147401, 11657844572835578076, 119294583757094535 }, + .{ 7648316884775828921, 15264332483297977688, 247182277434709002, 121630231312217685 }, + .{ 17410896758132241352, 10923914482914417070, 13976383996795783649, 124011608097704390 }, + .{ 9542674537907272703, 3079432708831728956, 14235189590642919676, 126439609438067572 }, + .{ 10364666969937261816, 8464573184892924210, 12758646866025101190, 128915148187220428 }, + .{ 14720354822146013883, 11480204489231511423, 7449876034836187038, 131439155071681461 }, + .{ 1692907053653558553, 17835392458598425233, 1754856712536736598, 134012579040499057 }, + .{ 5620591334531458755, 11361776175667106627, 13350215315297937856, 136636387622027174 }, + .{ 17455759733928092601, 10362573084069962561, 11246018728801810510, 139311567287686283 }, + .{ 2465404073814044982, 17694822665274381860, 1509954037718722697, 142039123822846312 }, + .{ 2152236053329638369, 11202280800589637091, 16388426812920420176, 72410041352485523 }, + .{ 17319024055671609028, 10944982848661280484, 2457150158022562661, 73827744744583080 }, + .{ 17511219308535248024, 5122059497846768077, 2089605804219668451, 75273205100637900 }, + .{ 10082673333144031533, 14429008783411894887, 12842832230171903890, 76746965869337783 }, + .{ 16196653406315961184, 10260180891682904501, 10537411930446752461, 78249581139456266 }, + .{ 15084422041749743389, 234835370106753111, 16662517110286225617, 79781615848172976 }, + .{ 8199644021067702606, 3787318116274991885, 7438130039325743106, 81343645993472659 }, + .{ 12039493937039359765, 9773822153580393709, 5945428874398357806, 82936258850702722 }, + .{ 984543865091303961, 7975107621689454830, 6556665988501773347, 84560053193370726 }, + .{ 9633317878125234244, 16099592426808915028, 9706674539190598200, 86215639518264828 }, + .{ 6860695058870476186, 4471839111886709592, 7828342285492709568, 87903640274981819 }, + .{ 14583324717644598331, 4496120889473451238, 5290040788305728466, 89624690099949049 }, + .{ 18093669366515003715, 12879506572606942994, 18005739787089675377, 91379436055028227 }, + .{ 17997493966862379937, 14646222655265145582, 10265023312844161858, 93168537870790806 }, + .{ 12283848109039722318, 11290258077250314935, 9878160025624946825, 94992668194556404 }, + .{ 8087752761883078164, 5262596608437575693, 11093553063763274413, 96852512843287537 }, + .{ 15027787746776840781, 12250273651168257752, 9290470558712181914, 98748771061435726 }, + .{ 15003915578366724489, 2937334162439764327, 5404085603526796602, 100682155783835929 }, + .{ 5225610465224746757, 14932114897406142027, 2774647558180708010, 102653393903748137 }, + .{ 17112957703385190360, 12069082008339002412, 3901112447086388439, 104663226546146909 }, + .{ 4062324464323300238, 3992768146772240329, 15757196565593695724, 106712409346361594 }, + .{ 5525364615810306701, 11855206026704935156, 11344868740897365300, 108801712734172003 }, + .{ 9274143661888462646, 4478365862348432381, 18010077872551661771, 110931922223466333 }, + .{ 12604141221930060148, 8930937759942591500, 9382183116147201338, 113103838707570263 }, + .{ 14513929377491886653, 1410646149696279084, 587092196850797612, 115318278760358235 }, + .{ 2226851524999454362, 7717102471110805679, 7187441550995571734, 117576074943260147 }, + .{ 5527526061344932763, 2347100676188369132, 16976241418824030445, 119878076118278875 }, + .{ 6088479778147221611, 17669593130014777580, 10991124207197663546, 122225147767136307 }, + .{ 11107734086759692041, 3391795220306863431, 17233960908859089158, 124618172316667879 }, + .{ 7913172514655155198, 17726879005381242552, 641069866244011540, 127058049470587962 }, + .{ 12596991768458713949, 15714785522479904446, 6035972567136116512, 129545696547750811 }, + .{ 16901996933781815980, 4275085211437148707, 14091642539965169063, 132082048827034281 }, + .{ 7524574627987869240, 15661204384239316051, 2444526454225712267, 134668059898975949 }, + .{ 8199251625090479942, 6803282222165044067, 16064817666437851504, 137304702024293857 }, + .{ 4453256673338111920, 15269922543084434181, 3139961729834750852, 139992966499426682 }, + .{ 15841763546372731299, 3013174075437671812, 4383755396295695606, 142733864029230733 }, + .{ 9771896230907310329, 4900659362437687569, 12386126719044266361, 72764212553486967 }, + .{ 9420455527449565190, 1859606122611023693, 6555040298902684281, 74188850200884818 }, + .{ 5146105983135678095, 2287300449992174951, 4325371679080264751, 75641380576797959 }, + .{ 11019359372592553360, 8422686425957443718, 7175176077944048210, 77122349788024458 }, + .{ 11005742969399620716, 4132174559240043701, 9372258443096612118, 78632314633490790 }, + .{ 8887589641394725840, 8029899502466543662, 14582206497241572853, 80171842813591127 }, + .{ 360247523705545899, 12568341805293354211, 14653258284762517866, 81741513143625247 }, + .{ 12314272731984275834, 4740745023227177044, 6141631472368337539, 83341915771415304 }, + .{ 441052047733984759, 7940090120939869826, 11750200619921094248, 84973652399183278 }, + .{ 3436657868127012749, 9187006432149937667, 16389726097323041290, 86637336509772529 }, + .{ 13490220260784534044, 15339072891382896702, 8846102360835316895, 88333593597298497 }, + .{ 4125672032094859833, 158347675704003277, 10592598512749774447, 90063061402315272 }, + .{ 12189928252974395775, 2386931199439295891, 7009030566469913276, 91826390151586454 }, + .{ 9256479608339282969, 2844900158963599229, 11148388908923225596, 93624242802550437 }, + .{ 11584393507658707408, 2863659090805147914, 9873421561981063551, 95457295292572042 }, + .{ 13984297296943171390, 1931468383973130608, 12905719743235082319, 97326236793074198 }, + .{ 5837045222254987499, 10213498696735864176, 14893951506257020749, 99231769968645227 }, +}; + +// Unfortunately, the results are sometimes off by one or two. We use an additional +// lookup table to store those cases and adjust the result. +const POW5_ERRORS: [156]u64 = .{ + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x9555596400000000, + 0x65a6569525565555, 0x4415551445449655, 0x5105015504144541, 0x65a69969a6965964, + 0x5054955969959656, 0x5105154515554145, 0x4055511051591555, 0x5500514455550115, + 0x0041140014145515, 0x1005440545511051, 0x0014405450411004, 0x0414440010500000, + 0x0044000440010040, 0x5551155000004001, 0x4554555454544114, 0x5150045544005441, + 0x0001111400054501, 0x6550955555554554, 0x1504159645559559, 0x4105055141454545, + 0x1411541410405454, 0x0415555044545555, 0x0014154115405550, 0x1540055040411445, + 0x0000000500000000, 0x5644000000000000, 0x1155555591596555, 0x0410440054569565, + 0x5145100010010005, 0x0555041405500150, 0x4141450455140450, 0x0000000144000140, + 0x5114004001105410, 0x4444100404005504, 0x0414014410001015, 0x5145055155555015, + 0x0141041444445540, 0x0000100451541414, 0x4105041104155550, 0x0500501150451145, + 0x1001050000004114, 0x5551504400141045, 0x5110545410151454, 0x0100001400004040, + 0x5040010111040000, 0x0140000150541100, 0x4400140400104110, 0x5011014405545004, + 0x0000000044155440, 0x0000000010000000, 0x1100401444440001, 0x0040401010055111, + 0x5155155551405454, 0x0444440015514411, 0x0054505054014101, 0x0451015441115511, + 0x1541411401140551, 0x4155104514445110, 0x4141145450145515, 0x5451445055155050, + 0x4400515554110054, 0x5111145104501151, 0x565a655455500501, 0x5565555555525955, + 0x0550511500405695, 0x4415504051054544, 0x6555595965555554, 0x0100915915555655, + 0x5540001510001001, 0x5450051414000544, 0x1405010555555551, 0x5555515555644155, + 0x5555055595496555, 0x5451045004415000, 0x5450510144040144, 0x5554155555556455, + 0x5051555495415555, 0x5555554555555545, 0x0000000010005455, 0x4000005000040000, + 0x5565555555555954, 0x5554559555555505, 0x9645545495552555, 0x4000400055955564, + 0x0040000000000001, 0x4004100100000000, 0x5540040440000411, 0x4565555955545644, + 0x1140659549651556, 0x0100000410010000, 0x5555515400004001, 0x5955545555155255, + 0x5151055545505556, 0x5051454510554515, 0x0501500050415554, 0x5044154005441005, + 0x1455445450550455, 0x0010144055144545, 0x0000401100000004, 0x1050145050000010, + 0x0415004554011540, 0x1000510100151150, 0x0100040400001144, 0x0000000000000000, + 0x0550004400000100, 0x0151145041451151, 0x0000400400005450, 0x0000100044010004, + 0x0100054100050040, 0x0504400005410010, 0x4011410445500105, 0x0000404000144411, + 0x0101504404500000, 0x0000005044400400, 0x0000000014000100, 0x0404440414000000, + 0x5554100410000140, 0x4555455544505555, 0x5454105055455455, 0x0115454155454015, + 0x4404110000045100, 0x4400001100101501, 0x6596955956966a94, 0x0040655955665965, + 0x5554144400100155, 0xa549495401011041, 0x5596555565955555, 0x5569965959549555, + 0x969565a655555456, 0x0000001000000000, 0x0000000040000140, 0x0000040100000000, + 0x1415454400000000, 0x5410415411454114, 0x0400040104000154, 0x0504045000000411, + 0x0000001000000010, 0x5554000000001040, 0x5549155551556595, 0x1455541055515555, + 0x0510555454554541, 0x9555555555540455, 0x6455456555556465, 0x4524565555654514, + 0x5554655255559545, 0x9555455441155556, 0x0000000051515555, 0x0010005040000550, + 0x5044044040000000, 0x1045040440010500, 0x0000400000040000, 0x0000000000000000, +}; + +const GENERIC_POW5_INV_SPLIT: [89][4]u64 = .{ + .{ 0, 0, 0, 144115188075855872 }, + .{ 1573859546583440065, 2691002611772552616, 6763753280790178510, 141347765182270746 }, + .{ 12960290449513840412, 12345512957918226762, 18057899791198622765, 138633484706040742 }, + .{ 7615871757716765416, 9507132263365501332, 4879801712092008245, 135971326161092377 }, + .{ 7869961150745287587, 5804035291554591636, 8883897266325833928, 133360288657597085 }, + .{ 2942118023529634767, 15128191429820565086, 10638459445243230718, 130799390525667397 }, + .{ 14188759758411913794, 5362791266439207815, 8068821289119264054, 128287668946279217 }, + .{ 7183196927902545212, 1952291723540117099, 12075928209936341512, 125824179589281448 }, + .{ 5672588001402349748, 17892323620748423487, 9874578446960390364, 123407996258356868 }, + .{ 4442590541217566325, 4558254706293456445, 10343828952663182727, 121038210542800766 }, + .{ 3005560928406962566, 2082271027139057888, 13961184524927245081, 118713931475986426 }, + .{ 13299058168408384786, 17834349496131278595, 9029906103900731664, 116434285200389047 }, + .{ 5414878118283973035, 13079825470227392078, 17897304791683760280, 114198414639042157 }, + .{ 14609755883382484834, 14991702445765844156, 3269802549772755411, 112005479173303009 }, + .{ 15967774957605076027, 2511532636717499923, 16221038267832563171, 109854654326805788 }, + .{ 9269330061621627145, 3332501053426257392, 16223281189403734630, 107745131455483836 }, + .{ 16739559299223642282, 1873986623300664530, 6546709159471442872, 105676117443544318 }, + .{ 17116435360051202055, 1359075105581853924, 2038341371621886470, 103646834405281051 }, + .{ 17144715798009627550, 3201623802661132408, 9757551605154622431, 101656519392613377 }, + .{ 17580479792687825857, 6546633380567327312, 15099972427870912398, 99704424108241124 }, + .{ 9726477118325522902, 14578369026754005435, 11728055595254428803, 97789814624307808 }, + .{ 134593949518343635, 5715151379816901985, 1660163707976377376, 95911971106466306 }, + .{ 5515914027713859358, 7124354893273815720, 5548463282858794077, 94070187543243255 }, + .{ 6188403395862945512, 5681264392632320838, 15417410852121406654, 92263771480600430 }, + .{ 15908890877468271457, 10398888261125597540, 4817794962769172309, 90492043761593298 }, + .{ 1413077535082201005, 12675058125384151580, 7731426132303759597, 88754338271028867 }, + .{ 1486733163972670293, 11369385300195092554, 11610016711694864110, 87050001685026843 }, + .{ 8788596583757589684, 3978580923851924802, 9255162428306775812, 85378393225389919 }, + .{ 7203518319660962120, 15044736224407683725, 2488132019818199792, 83738884418690858 }, + .{ 4004175967662388707, 18236988667757575407, 15613100370957482671, 82130858859985791 }, + .{ 18371903370586036463, 53497579022921640, 16465963977267203307, 80553711981064899 }, + .{ 10170778323887491315, 1999668801648976001, 10209763593579456445, 79006850823153334 }, + .{ 17108131712433974546, 16825784443029944237, 2078700786753338945, 77489693813976938 }, + .{ 17221789422665858532, 12145427517550446164, 5391414622238668005, 76001670549108934 }, + .{ 4859588996898795878, 1715798948121313204, 3950858167455137171, 74542221577515387 }, + .{ 13513469241795711526, 631367850494860526, 10517278915021816160, 73110798191218799 }, + .{ 11757513142672073111, 2581974932255022228, 17498959383193606459, 143413724438001539 }, + .{ 14524355192525042817, 5640643347559376447, 1309659274756813016, 140659771648132296 }, + .{ 2765095348461978538, 11021111021896007722, 3224303603779962366, 137958702611185230 }, + .{ 12373410389187981037, 13679193545685856195, 11644609038462631561, 135309501808182158 }, + .{ 12813176257562780151, 3754199046160268020, 9954691079802960722, 132711173221007413 }, + .{ 17557452279667723458, 3237799193992485824, 17893947919029030695, 130162739957935629 }, + .{ 14634200999559435155, 4123869946105211004, 6955301747350769239, 127663243886350468 }, + .{ 2185352760627740240, 2864813346878886844, 13049218671329690184, 125211745272516185 }, + .{ 6143438674322183002, 10464733336980678750, 6982925169933978309, 122807322428266620 }, + .{ 1099509117817174576, 10202656147550524081, 754997032816608484, 120449071364478757 }, + .{ 2410631293559367023, 17407273750261453804, 15307291918933463037, 118136105451200587 }, + .{ 12224968375134586697, 1664436604907828062, 11506086230137787358, 115867555084305488 }, + .{ 3495926216898000888, 18392536965197424288, 10992889188570643156, 113642567358547782 }, + .{ 8744506286256259680, 3966568369496879937, 18342264969761820037, 111460305746896569 }, + .{ 7689600520560455039, 5254331190877624630, 9628558080573245556, 109319949786027263 }, + .{ 11862637625618819436, 3456120362318976488, 14690471063106001082, 107220694767852583 }, + .{ 5697330450030126444, 12424082405392918899, 358204170751754904, 105161751436977040 }, + .{ 11257457505097373622, 15373192700214208870, 671619062372033814, 103142345693961148 }, + .{ 16850355018477166700, 1913910419361963966, 4550257919755970531, 101161718304283822 }, + .{ 9670835567561997011, 10584031339132130638, 3060560222974851757, 99219124612893520 }, + .{ 7698686577353054710, 11689292838639130817, 11806331021588878241, 97313834264240819 }, + .{ 12233569599615692137, 3347791226108469959, 10333904326094451110, 95445130927687169 }, + .{ 13049400362825383933, 17142621313007799680, 3790542585289224168, 93612312028186576 }, + .{ 12430457242474442072, 5625077542189557960, 14765055286236672238, 91814688482138969 }, + .{ 4759444137752473128, 2230562561567025078, 4954443037339580076, 90051584438315940 }, + .{ 7246913525170274758, 8910297835195760709, 4015904029508858381, 88322337023761438 }, + .{ 12854430245836432067, 8135139748065431455, 11548083631386317976, 86626296094571907 }, + .{ 4848827254502687803, 4789491250196085625, 3988192420450664125, 84962823991462151 }, + .{ 7435538409611286684, 904061756819742353, 14598026519493048444, 83331295300025028 }, + .{ 11042616160352530997, 8948390828345326218, 10052651191118271927, 81731096615594853 }, + .{ 11059348291563778943, 11696515766184685544, 3783210511290897367, 80161626312626082 }, + .{ 7020010856491885826, 5025093219346041680, 8960210401638911765, 78622294318500592 }, + .{ 17732844474490699984, 7820866704994446502, 6088373186798844243, 77112521891678506 }, + .{ 688278527545590501, 3045610706602776618, 8684243536999567610, 75631741404109150 }, + .{ 2734573255120657297, 3903146411440697663, 9470794821691856713, 74179396127820347 }, + .{ 15996457521023071259, 4776627823451271680, 12394856457265744744, 72754940025605801 }, + .{ 13492065758834518331, 7390517611012222399, 1630485387832860230, 142715675091463768 }, + .{ 13665021627282055864, 9897834675523659302, 17907668136755296849, 139975126841173266 }, + .{ 9603773719399446181, 10771916301484339398, 10672699855989487527, 137287204938390542 }, + .{ 3630218541553511265, 8139010004241080614, 2876479648932814543, 134650898807055963 }, + .{ 8318835909686377084, 9525369258927993371, 2796120270400437057, 132065217277054270 }, + .{ 11190003059043290163, 12424345635599592110, 12539346395388933763, 129529188211565064 }, + .{ 8701968833973242276, 820569587086330727, 2315591597351480110, 127041858141569228 }, + .{ 5115113890115690487, 16906305245394587826, 9899749468931071388, 124602291907373862 }, + .{ 15543535488939245974, 10945189844466391399, 3553863472349432246, 122209572307020975 }, + .{ 7709257252608325038, 1191832167690640880, 15077137020234258537, 119862799751447719 }, + .{ 7541333244210021737, 9790054727902174575, 5160944773155322014, 117561091926268545 }, + .{ 12297384708782857832, 1281328873123467374, 4827925254630475769, 115303583460052092 }, + .{ 13243237906232367265, 15873887428139547641, 3607993172301799599, 113089425598968120 }, + .{ 11384616453739611114, 15184114243769211033, 13148448124803481057, 110917785887682141 }, + .{ 17727970963596660683, 1196965221832671990, 14537830463956404138, 108787847856377790 }, + .{ 17241367586707330931, 8880584684128262874, 11173506540726547818, 106698810713789254 }, + .{ 7184427196661305643, 14332510582433188173, 14230167953789677901, 104649889046128358 }, +}; + +const POW5_INV_ERRORS: [154]u64 = .{ + 0x1144155514145504, 0x0000541555401141, 0x0000000000000000, 0x0154454000000000, + 0x4114105515544440, 0x0001001111500415, 0x4041411410011000, 0x5550114515155014, + 0x1404100041554551, 0x0515000450404410, 0x5054544401140004, 0x5155501005555105, + 0x1144141000105515, 0x0541500000500000, 0x1104105540444140, 0x4000015055514110, + 0x0054010450004005, 0x4155515404100005, 0x5155145045155555, 0x1511555515440558, + 0x5558544555515555, 0x0000000000000010, 0x5004000000000050, 0x1415510100000010, + 0x4545555444514500, 0x5155151555555551, 0x1441540144044554, 0x5150104045544400, + 0x5450545401444040, 0x5554455045501400, 0x4655155555555145, 0x1000010055455055, + 0x1000004000055004, 0x4455405104000005, 0x4500114504150545, 0x0000000014000000, + 0x5450000000000000, 0x5514551511445555, 0x4111501040555451, 0x4515445500054444, + 0x5101500104100441, 0x1545115155545055, 0x0000000000000000, 0x1554000000100000, + 0x5555545595551555, 0x5555051851455955, 0x5555555555555559, 0x0000400011001555, + 0x0000004400040000, 0x5455511555554554, 0x5614555544115445, 0x6455156145555155, + 0x5455855455415455, 0x5515555144555545, 0x0114400000145155, 0x0000051000450511, + 0x4455154554445100, 0x4554150141544455, 0x65955555559a5965, 0x5555555854559559, + 0x9569654559616595, 0x1040044040005565, 0x1010010500011044, 0x1554015545154540, + 0x4440555401545441, 0x1014441450550105, 0x4545400410504145, 0x5015111541040151, + 0x5145051154000410, 0x1040001044545044, 0x4001400000151410, 0x0540000044040000, + 0x0510555454411544, 0x0400054054141550, 0x1001041145001100, 0x0000000140000000, + 0x0000000014100000, 0x1544005454000140, 0x4050055505445145, 0x0011511104504155, + 0x5505544415045055, 0x1155154445515554, 0x0000000000004555, 0x0000000000000000, + 0x5101010510400004, 0x1514045044440400, 0x5515519555515555, 0x4554545441555545, + 0x1551055955551515, 0x0150000011505515, 0x0044005040400000, 0x0004001004010050, + 0x0000051004450414, 0x0114001101001144, 0x0401000001000001, 0x4500010001000401, + 0x0004100000005000, 0x0105000441101100, 0x0455455550454540, 0x5404050144105505, + 0x4101510540555455, 0x1055541411451555, 0x5451445110115505, 0x1154110010101545, + 0x1145140450054055, 0x5555565415551554, 0x1550559555555555, 0x5555541545045141, + 0x4555455450500100, 0x5510454545554555, 0x1510140115045455, 0x1001050040111510, + 0x5555454555555504, 0x9954155545515554, 0x6596656555555555, 0x0140410051555559, + 0x0011104010001544, 0x965669659a680501, 0x5655a55955556955, 0x4015111014404514, + 0x1414155554505145, 0x0540040011051404, 0x1010000000015005, 0x0010054050004410, + 0x5041104014000100, 0x4440010500100001, 0x1155510504545554, 0x0450151545115541, + 0x4000100400110440, 0x1004440010514440, 0x0000115050450000, 0x0545404455541500, + 0x1051051555505101, 0x5505144554544144, 0x4550545555515550, 0x0015400450045445, + 0x4514155400554415, 0x4555055051050151, 0x1511441450001014, 0x4544554510404414, + 0x4115115545545450, 0x5500541555551555, 0x5550010544155015, 0x0144414045545500, + 0x4154050001050150, 0x5550511111000145, 0x1114504055000151, 0x5104041101451040, + 0x0010501401051441, 0x0010501450504401, 0x4554585440044444, 0x5155555951450455, + 0x0040000400105555, 0x0000000000000001, +}; + +// zig fmt: on + +fn check(comptime T: type, value: T, comptime expected: []const u8) !void { + const I = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } }); + + var buf: [6000]u8 = undefined; + const value_bits: I = @bitCast(value); + const s = try formatFloat(&buf, value, .{}); + try std.testing.expectEqualStrings(expected, s); + + if (@bitSizeOf(T) != 80) { + const o = try std.fmt.parseFloat(T, s); + const o_bits: I = @bitCast(o); + + if (std.math.isNan(value)) { + try std.testing.expect(std.math.isNan(o)); + } else { + try std.testing.expectEqual(value_bits, o_bits); + } + } +} + +test "format f32" { + try check(f32, 0.0, "0e0"); + try check(f32, -0.0, "-0e0"); + try check(f32, 1.0, "1e0"); + try check(f32, -1.0, "-1e0"); + try check(f32, std.math.nan(f32), "nan"); + try check(f32, std.math.inf(f32), "inf"); + try check(f32, -std.math.inf(f32), "-inf"); + try check(f32, 1.1754944e-38, "1.1754944e-38"); + try check(f32, @bitCast(@as(u32, 0x7f7fffff)), "3.4028235e38"); + try check(f32, @bitCast(@as(u32, 1)), "1e-45"); + try check(f32, 3.355445E7, "3.355445e7"); + try check(f32, 8.999999e9, "9e9"); + try check(f32, 3.4366717e10, "3.436672e10"); + try check(f32, 3.0540412e5, "3.0540412e5"); + try check(f32, 8.0990312e3, "8.0990312e3"); + try check(f32, 2.4414062e-4, "2.4414062e-4"); + try check(f32, 2.4414062e-3, "2.4414062e-3"); + try check(f32, 4.3945312e-3, "4.3945312e-3"); + try check(f32, 6.3476562e-3, "6.3476562e-3"); + try check(f32, 4.7223665e21, "4.7223665e21"); + try check(f32, 8388608.0, "8.388608e6"); + try check(f32, 1.6777216e7, "1.6777216e7"); + try check(f32, 3.3554436e7, "3.3554436e7"); + try check(f32, 6.7131496e7, "6.7131496e7"); + try check(f32, 1.9310392e-38, "1.9310392e-38"); + try check(f32, -2.47e-43, "-2.47e-43"); + try check(f32, 1.993244e-38, "1.993244e-38"); + try check(f32, 4103.9003, "4.1039004e3"); + try check(f32, 5.3399997e9, "5.3399997e9"); + try check(f32, 6.0898e-39, "6.0898e-39"); + try check(f32, 0.0010310042, "1.0310042e-3"); + try check(f32, 2.8823261e17, "2.882326e17"); + try check(f32, 7.038531e-26, "7.038531e-26"); + try check(f32, 9.2234038e17, "9.223404e17"); + try check(f32, 6.7108872e7, "6.710887e7"); + try check(f32, 1.0e-44, "1e-44"); + try check(f32, 2.816025e14, "2.816025e14"); + try check(f32, 9.223372e18, "9.223372e18"); + try check(f32, 1.5846085e29, "1.5846086e29"); + try check(f32, 1.1811161e19, "1.1811161e19"); + try check(f32, 5.368709e18, "5.368709e18"); + try check(f32, 4.6143165e18, "4.6143166e18"); + try check(f32, 0.007812537, "7.812537e-3"); + try check(f32, 1.4e-45, "1e-45"); + try check(f32, 1.18697724e20, "1.18697725e20"); + try check(f32, 1.00014165e-36, "1.00014165e-36"); + try check(f32, 200.0, "2e2"); + try check(f32, 3.3554432e7, "3.3554432e7"); + + try check(f32, 1.0, "1e0"); + try check(f32, 1.2, "1.2e0"); + try check(f32, 1.23, "1.23e0"); + try check(f32, 1.234, "1.234e0"); + try check(f32, 1.2345, "1.2345e0"); + try check(f32, 1.23456, "1.23456e0"); + try check(f32, 1.234567, "1.234567e0"); + try check(f32, 1.2345678, "1.2345678e0"); + try check(f32, 1.23456735e-36, "1.23456735e-36"); +} + +test "format f64" { + try check(f64, 0.0, "0e0"); + try check(f64, -0.0, "-0e0"); + try check(f64, 1.0, "1e0"); + try check(f64, -1.0, "-1e0"); + try check(f64, std.math.nan(f64), "nan"); + try check(f64, std.math.inf(f64), "inf"); + try check(f64, -std.math.inf(f64), "-inf"); + try check(f64, 2.2250738585072014e-308, "2.2250738585072014e-308"); + try check(f64, @bitCast(@as(u64, 0x7fefffffffffffff)), "1.7976931348623157e308"); + try check(f64, @bitCast(@as(u64, 1)), "5e-324"); + try check(f64, 2.98023223876953125e-8, "2.9802322387695312e-8"); + try check(f64, -2.109808898695963e16, "-2.109808898695963e16"); + try check(f64, 4.940656e-318, "4.940656e-318"); + try check(f64, 1.18575755e-316, "1.18575755e-316"); + try check(f64, 2.989102097996e-312, "2.989102097996e-312"); + try check(f64, 9.0608011534336e15, "9.0608011534336e15"); + try check(f64, 4.708356024711512e18, "4.708356024711512e18"); + try check(f64, 9.409340012568248e18, "9.409340012568248e18"); + try check(f64, 1.2345678, "1.2345678e0"); + try check(f64, @bitCast(@as(u64, 0x4830f0cf064dd592)), "5.764607523034235e39"); + try check(f64, @bitCast(@as(u64, 0x4840f0cf064dd592)), "1.152921504606847e40"); + try check(f64, @bitCast(@as(u64, 0x4850f0cf064dd592)), "2.305843009213694e40"); + + try check(f64, 1, "1e0"); + try check(f64, 1.2, "1.2e0"); + try check(f64, 1.23, "1.23e0"); + try check(f64, 1.234, "1.234e0"); + try check(f64, 1.2345, "1.2345e0"); + try check(f64, 1.23456, "1.23456e0"); + try check(f64, 1.234567, "1.234567e0"); + try check(f64, 1.2345678, "1.2345678e0"); + try check(f64, 1.23456789, "1.23456789e0"); + try check(f64, 1.234567895, "1.234567895e0"); + try check(f64, 1.2345678901, "1.2345678901e0"); + try check(f64, 1.23456789012, "1.23456789012e0"); + try check(f64, 1.234567890123, "1.234567890123e0"); + try check(f64, 1.2345678901234, "1.2345678901234e0"); + try check(f64, 1.23456789012345, "1.23456789012345e0"); + try check(f64, 1.234567890123456, "1.234567890123456e0"); + try check(f64, 1.2345678901234567, "1.2345678901234567e0"); + + try check(f64, 4.294967294, "4.294967294e0"); + try check(f64, 4.294967295, "4.294967295e0"); + try check(f64, 4.294967296, "4.294967296e0"); + try check(f64, 4.294967297, "4.294967297e0"); + try check(f64, 4.294967298, "4.294967298e0"); +} + +test "format f80" { + try check(f80, 0.0, "0e0"); + try check(f80, -0.0, "-0e0"); + try check(f80, 1.0, "1e0"); + try check(f80, -1.0, "-1e0"); + try check(f80, std.math.nan(f80), "nan"); + try check(f80, std.math.inf(f80), "inf"); + try check(f80, -std.math.inf(f80), "-inf"); + + try check(f80, 2.2250738585072014e-308, "2.2250738585072014e-308"); + try check(f80, 2.98023223876953125e-8, "2.98023223876953125e-8"); + try check(f80, -2.109808898695963e16, "-2.109808898695963e16"); + try check(f80, 4.940656e-318, "4.940656e-318"); + try check(f80, 1.18575755e-316, "1.18575755e-316"); + try check(f80, 2.989102097996e-312, "2.989102097996e-312"); + try check(f80, 9.0608011534336e15, "9.0608011534336e15"); + try check(f80, 4.708356024711512e18, "4.708356024711512e18"); + try check(f80, 9.409340012568248e18, "9.409340012568248e18"); + try check(f80, 1.2345678, "1.2345678e0"); +} + +test "format f128" { + try check(f128, 0.0, "0e0"); + try check(f128, -0.0, "-0e0"); + try check(f128, 1.0, "1e0"); + try check(f128, -1.0, "-1e0"); + try check(f128, std.math.nan(f128), "nan"); + try check(f128, std.math.inf(f128), "inf"); + try check(f128, -std.math.inf(f128), "-inf"); + + try check(f128, 2.2250738585072014e-308, "2.2250738585072014e-308"); + try check(f128, 2.98023223876953125e-8, "2.98023223876953125e-8"); + try check(f128, -2.109808898695963e16, "-2.109808898695963e16"); + try check(f128, 4.940656e-318, "4.940656e-318"); + try check(f128, 1.18575755e-316, "1.18575755e-316"); + try check(f128, 2.989102097996e-312, "2.989102097996e-312"); + try check(f128, 9.0608011534336e15, "9.0608011534336e15"); + try check(f128, 4.708356024711512e18, "4.708356024711512e18"); + try check(f128, 9.409340012568248e18, "9.409340012568248e18"); + try check(f128, 1.2345678, "1.2345678e0"); +} + +test "format float to decimal with zero precision" { + try expectFmt("5", "{d:.0}", .{5}); + try expectFmt("6", "{d:.0}", .{6}); + try expectFmt("7", "{d:.0}", .{7}); + try expectFmt("8", "{d:.0}", .{8}); +} diff --git a/lib/std/fmt/ryu128.zig b/lib/std/fmt/ryu128.zig @@ -1,1139 +0,0 @@ -//! This file implements the ryu floating point conversion algorithm: -//! https://dl.acm.org/doi/pdf/10.1145/3360595 - -const std = @import("std"); -const expectFmt = std.testing.expectFmt; - -const special_exponent = 0x7fffffff; - -/// Any buffer used for `format` must be at least this large. This is asserted. A runtime check will -/// additionally be performed if more bytes are required. -pub const min_buffer_size = 53; - -/// Returns the minimum buffer size needed to print every float of a specific type and format. -pub fn bufferSize(comptime mode: Format, comptime T: type) comptime_int { - comptime std.debug.assert(@typeInfo(T) == .Float); - return switch (mode) { - .scientific => 53, - // Based on minimum subnormal values. - .decimal => switch (@bitSizeOf(T)) { - 16 => @max(15, min_buffer_size), - 32 => 55, - 64 => 347, - 80 => 4996, - 128 => 5011, - else => unreachable, - }, - }; -} - -pub const RyuError = error{ - BufferTooSmall, -}; - -pub const Format = enum { - scientific, - decimal, -}; - -pub const FormatOptions = struct { - mode: Format = .scientific, - precision: ?usize = null, -}; - -/// Format a floating-point value and write it to buffer. Returns a slice to the buffer containing -/// the string representation. -/// -/// Full precision is the default. Any full precision float can be reparsed with std.fmt.parseFloat -/// unambiguously. -/// -/// Scientific mode is recommended generally as the output is more compact and any type can be -/// written in full precision using a buffer of only `min_buffer_size`. -/// -/// When printing full precision decimals, use `bufferSize` to get the required space. It is -/// recommended to bound decimal output with a fixed precision to reduce the required buffer size. -pub fn format(buf: []u8, v_: anytype, options: FormatOptions) RyuError![]const u8 { - const v = switch (@TypeOf(v_)) { - // comptime_float internally is a f128; this preserves precision. - comptime_float => @as(f128, v_), - else => v_, - }; - - const T = @TypeOf(v); - comptime std.debug.assert(@typeInfo(T) == .Float); - const I = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } }); - - const has_explicit_leading_bit = std.math.floatMantissaBits(T) - std.math.floatFractionalBits(T) != 0; - const d = binaryToDecimal(@as(I, @bitCast(v)), std.math.floatMantissaBits(T), std.math.floatExponentBits(T), has_explicit_leading_bit); - - return switch (options.mode) { - .scientific => formatScientific(buf, d, options.precision), - .decimal => formatDecimal(buf, d, options.precision), - }; -} - -pub const FloatDecimal128 = struct { - mantissa: u128, - exponent: i32, - sign: bool, -}; - -fn copySpecialStr(buf: []u8, f: FloatDecimal128) []const u8 { - if (f.sign) { - buf[0] = '-'; - } - const offset: usize = @intFromBool(f.sign); - if (f.mantissa != 0) { - @memcpy(buf[offset..][0..3], "nan"); - return buf[0 .. 3 + offset]; - } - @memcpy(buf[offset..][0..3], "inf"); - return buf[0 .. 3 + offset]; -} - -fn writeDecimal(buf: []u8, value: anytype, count: usize) void { - var i: usize = 0; - - while (i + 2 < count) : (i += 2) { - const c: u8 = @intCast(value.* % 100); - value.* /= 100; - const d = std.fmt.digits2(c); - buf[count - i - 1] = d[1]; - buf[count - i - 2] = d[0]; - } - - while (i < count) : (i += 1) { - const c: u8 = @intCast(value.* % 10); - value.* /= 10; - buf[count - i - 1] = '0' + c; - } -} - -fn isPowerOf10(n_: u128) bool { - var n = n_; - while (n != 0) : (n /= 10) { - if (n % 10 != 0) return false; - } - return true; -} - -const RoundMode = enum { - /// 1234.56 = precision 2 - decimal, - /// 1.23456e3 = precision 5 - scientific, -}; - -fn round(f: FloatDecimal128, mode: RoundMode, precision: usize) FloatDecimal128 { - var round_digit: usize = 0; - var output = f.mantissa; - var exp = f.exponent; - const olength = decimalLength(output); - - switch (mode) { - .decimal => { - if (f.exponent > 0) { - round_digit = (olength - 1) + precision + @as(usize, @intCast(f.exponent)); - } else { - const min_exp_required = @as(usize, @intCast(-f.exponent)); - if (precision + olength > min_exp_required) { - round_digit = precision + olength - min_exp_required; - } - } - }, - .scientific => { - round_digit = 1 + precision; - }, - } - - if (round_digit < olength) { - var nlength = olength; - for (round_digit + 1..olength) |_| { - output /= 10; - exp += 1; - nlength -= 1; - } - - if (output % 10 >= 5) { - output /= 10; - output += 1; - exp += 1; - - // e.g. 9999 -> 10000 - if (isPowerOf10(output)) { - output /= 10; - exp += 1; - } - } - } - - return .{ - .mantissa = output, - .exponent = exp, - .sign = f.sign, - }; -} - -/// Write a FloatDecimal128 to a buffer in scientific form. -/// -/// The buffer provided must be greater than `min_buffer_size` in length. If no precision is -/// specified, this function will never return an error. If a precision is specified, up to -/// `8 + precision` bytes will be written to the buffer. An error will be returned if the content -/// will not fit. -/// -/// It is recommended to bound decimal formatting with an exact precision. -pub fn formatScientific(buf: []u8, f_: FloatDecimal128, precision: ?usize) RyuError![]const u8 { - std.debug.assert(buf.len >= min_buffer_size); - var f = f_; - - if (f.exponent == special_exponent) { - return copySpecialStr(buf, f); - } - - if (precision) |prec| { - f = round(f, .scientific, prec); - } - - var output = f.mantissa; - const olength = decimalLength(output); - - if (precision) |prec| { - // fixed bound: sign(1) + leading_digit(1) + point(1) + exp_sign(1) + exp_max(4) - const req_bytes = 8 + prec; - if (buf.len < req_bytes) { - return error.BufferTooSmall; - } - } - - // Step 5: Print the scientific representation - var index: usize = 0; - if (f.sign) { - buf[index] = '-'; - index += 1; - } - - // 1.12345 - writeDecimal(buf[index + 2 ..], &output, olength - 1); - buf[index] = '0' + @as(u8, @intCast(output % 10)); - buf[index + 1] = '.'; - index += 2; - const dp_index = index; - if (olength > 1) index += olength - 1 else index -= 1; - - if (precision) |prec| { - index += @intFromBool(olength == 1); - if (prec > olength - 1) { - const len = prec - (olength - 1); - @memset(buf[index..][0..len], '0'); - index += len; - } else { - index = dp_index + prec - @intFromBool(prec == 0); - } - } - - // e100 - buf[index] = 'e'; - index += 1; - var exp = f.exponent + @as(i32, @intCast(olength)) - 1; - if (exp < 0) { - buf[index] = '-'; - index += 1; - exp = -exp; - } - var uexp: u32 = @intCast(exp); - const elength = decimalLength(uexp); - writeDecimal(buf[index..], &uexp, elength); - index += elength; - - return buf[0..index]; -} - -/// Write a FloatDecimal128 to a buffer in decimal form. -/// -/// The buffer provided must be greater than `min_buffer_size` bytes in length. If no precision is -/// specified, this may still return an error. If precision is specified, `2 + precision` bytes will -/// always be written. -pub fn formatDecimal(buf: []u8, f_: FloatDecimal128, precision: ?usize) RyuError![]const u8 { - std.debug.assert(buf.len >= min_buffer_size); - var f = f_; - - if (f.exponent == special_exponent) { - return copySpecialStr(buf, f); - } - - if (precision) |prec| { - f = round(f, .decimal, prec); - } - - var output = f.mantissa; - const olength = decimalLength(output); - - // fixed bound: leading_digit(1) + point(1) - const req_bytes = if (f.exponent >= 0) - 2 + @abs(f.exponent) + olength + (precision orelse 0) - else - 2 + @max(@abs(f.exponent) + olength, precision orelse 0); - if (buf.len < req_bytes) { - return error.BufferTooSmall; - } - - // Step 5: Print the decimal representation - var index: usize = 0; - if (f.sign) { - buf[index] = '-'; - index += 1; - } - - const dp_offset = f.exponent + cast_i32(olength); - if (dp_offset <= 0) { - // 0.000001234 - buf[index] = '0'; - buf[index + 1] = '.'; - index += 2; - const dp_index = index; - - const dp_poffset: u32 = @intCast(-dp_offset); - @memset(buf[index..][0..dp_poffset], '0'); - index += dp_poffset; - writeDecimal(buf[index..], &output, olength); - index += olength; - - if (precision) |prec| { - const dp_written = index - dp_index; - if (prec > dp_written) { - @memset(buf[index..][0 .. prec - dp_written], '0'); - } - index = dp_index + prec - @intFromBool(prec == 0); - } - } else { - // 123456000 - const dp_uoffset: usize = @intCast(dp_offset); - if (dp_uoffset >= olength) { - writeDecimal(buf[index..], &output, olength); - index += olength; - @memset(buf[index..][0 .. dp_uoffset - olength], '0'); - index += dp_uoffset - olength; - - if (precision) |prec| { - if (prec != 0) { - buf[index] = '.'; - index += 1; - @memset(buf[index..][0..prec], '0'); - index += prec; - } - } - } else { - // 12345.6789 - writeDecimal(buf[index + dp_uoffset + 1 ..], &output, olength - dp_uoffset); - buf[index + dp_uoffset] = '.'; - const dp_index = index + dp_uoffset + 1; - writeDecimal(buf[index..], &output, dp_uoffset); - index += olength + 1; - - if (precision) |prec| { - const dp_written = olength - dp_uoffset; - if (prec > dp_written) { - @memset(buf[index..][0 .. prec - dp_written], '0'); - } - index = dp_index + prec - @intFromBool(prec == 0); - } - } - } - - return buf[0..index]; -} - -fn cast_i32(v: anytype) i32 { - return @intCast(v); -} - -/// Convert a binary float representation to decimal. -pub fn binaryToDecimal(bits: u128, mantissa_bits: u7, exponent_bits: u5, explicit_leading_bit: bool) FloatDecimal128 { - const bias = (@as(u32, 1) << (exponent_bits - 1)) - 1; - const ieee_sign = ((bits >> (mantissa_bits + exponent_bits)) & 1) != 0; - const ieee_mantissa = bits & ((@as(u128, 1) << mantissa_bits) - 1); - const ieee_exponent: u32 = @intCast((bits >> mantissa_bits) & ((@as(u128, 1) << exponent_bits) - 1)); - - if (ieee_exponent == 0 and ieee_mantissa == 0) { - return .{ - .mantissa = 0, - .exponent = 0, - .sign = ieee_sign, - }; - } - if (ieee_exponent == ((@as(u32, 1) << exponent_bits) - 1)) { - return .{ - .mantissa = if (explicit_leading_bit) ieee_mantissa & ((@as(u128, 1) << (mantissa_bits - 1)) - 1) else ieee_mantissa, - .exponent = 0x7fffffff, - .sign = ieee_sign, - }; - } - - var e2: i32 = undefined; - var m2: u128 = undefined; - if (explicit_leading_bit) { - if (ieee_exponent == 0) { - e2 = 1 - cast_i32(bias) - cast_i32(mantissa_bits) + 1 - 2; - } else { - e2 = cast_i32(ieee_exponent) - cast_i32(bias) - cast_i32(mantissa_bits) + 1 - 2; - } - m2 = ieee_mantissa; - } else { - if (ieee_exponent == 0) { - e2 = 1 - cast_i32(bias) - cast_i32(mantissa_bits) - 2; - m2 = ieee_mantissa; - } else { - e2 = cast_i32(ieee_exponent) - cast_i32(bias) - cast_i32(mantissa_bits) - 2; - m2 = (@as(u128, 1) << mantissa_bits) | ieee_mantissa; - } - } - const even = (m2 & 1) == 0; - const accept_bounds = even; - - // Step 2: Determine the interval of legal decimal representations. - const mv = 4 * m2; - const mm_shift: u1 = @intFromBool((ieee_mantissa != if (explicit_leading_bit) (@as(u128, 1) << (mantissa_bits - 1)) else 0) or (ieee_exponent == 0)); - - // Step 3: Convert to a decimal power base using 128-bit arithmetic. - var vr: u128 = undefined; - var vp: u128 = undefined; - var vm: u128 = undefined; - var e10: i32 = undefined; - var vm_is_trailing_zeros = false; - var vr_is_trailing_zeros = false; - if (e2 >= 0) { - const q: u32 = log10Pow2(@intCast(e2)) - @intFromBool(e2 > 3); - e10 = cast_i32(q); - const k: i32 = @intCast(FLOAT_128_POW5_INV_BITCOUNT + pow5Bits(q) - 1); - const i: u32 = @intCast(-e2 + cast_i32(q) + k); - - const pow5 = computeInvPow5(q); - vr = mulShift(4 * m2, &pow5, i); - vp = mulShift(4 * m2 + 2, &pow5, i); - vm = mulShift(4 * m2 - 1 - mm_shift, &pow5, i); - - if (q <= 55) { - if (mv % 5 == 0) { - vr_is_trailing_zeros = multipleOfPowerOf5(mv, q -% 1); - } else if (accept_bounds) { - vm_is_trailing_zeros = multipleOfPowerOf5(mv - 1 - mm_shift, q); - } else { - vp -= @intFromBool(multipleOfPowerOf5(mv + 2, q)); - } - } - } else { - const q: u32 = log10Pow5(@intCast(-e2)) - @intFromBool(-e2 > 1); - e10 = cast_i32(q) + e2; - const i: i32 = -e2 - cast_i32(q); - const k: i32 = cast_i32(pow5Bits(@intCast(i))) - FLOAT_128_POW5_BITCOUNT; - const j: u32 = @intCast(cast_i32(q) - k); - - const pow5 = computePow5(@intCast(i)); - vr = mulShift(4 * m2, &pow5, j); - vp = mulShift(4 * m2 + 2, &pow5, j); - vm = mulShift(4 * m2 - 1 - mm_shift, &pow5, j); - - if (q <= 1) { - vr_is_trailing_zeros = true; - if (accept_bounds) { - vm_is_trailing_zeros = mm_shift == 1; - } else { - vp -= 1; - } - } else if (q < 127) { - vr_is_trailing_zeros = multipleOfPowerOf2(mv, q - 1); - } - } - - // Step 4: Find the shortest decimal representation in the interval of legal representations. - var removed: u32 = 0; - var last_removed_digit: u8 = 0; - - while (vp / 10 > vm / 10) { - vm_is_trailing_zeros = vm_is_trailing_zeros and vm % 10 == 0; - vr_is_trailing_zeros = vr_is_trailing_zeros and last_removed_digit == 0; - last_removed_digit = @intCast(vr % 10); - vr /= 10; - vp /= 10; - vm /= 10; - removed += 1; - } - - if (vm_is_trailing_zeros) { - while (vm % 10 == 0) { - vr_is_trailing_zeros = vr_is_trailing_zeros and last_removed_digit == 0; - last_removed_digit = @intCast(vr % 10); - vr /= 10; - vp /= 10; - vm /= 10; - removed += 1; - } - } - - if (vr_is_trailing_zeros and (last_removed_digit == 5) and (vr % 2 == 0)) { - last_removed_digit = 4; - } - - return .{ - .mantissa = vr + @intFromBool((vr == vm and (!accept_bounds or !vm_is_trailing_zeros)) or last_removed_digit >= 5), - .exponent = e10 + cast_i32(removed), - .sign = ieee_sign, - }; -} - -fn decimalLength(v: u128) u32 { - const LARGEST_POW10 = (@as(u128, 5421010862427522170) << 64) | 687399551400673280; - var p10 = LARGEST_POW10; - var i: u32 = 39; - while (i > 0) : (i -= 1) { - if (v >= p10) return i; - p10 /= 10; - } - return 1; -} - -// floor(log_10(2^e)) -fn log10Pow2(e: u32) u32 { - std.debug.assert(e <= 1 << 15); - return @intCast((@as(u64, @intCast(e)) * 169464822037455) >> 49); -} - -// floor(log_10(5^e)) -fn log10Pow5(e: u32) u32 { - std.debug.assert(e <= 1 << 15); - return @intCast((@as(u64, @intCast(e)) * 196742565691928) >> 48); -} - -// if (e == 0) 1 else ceil(log_2(5^e)) -fn pow5Bits(e: u32) u32 { - std.debug.assert(e <= 1 << 15); - return @intCast(((@as(u64, @intCast(e)) * 163391164108059) >> 46) + 1); -} - -fn pow5Factor(value_: u128) u32 { - var count: u32 = 0; - var value = value_; - while (value > 0) : ({ - count += 1; - value /= 5; - }) { - if (value % 5 != 0) return count; - } - return 0; -} - -fn multipleOfPowerOf5(value: u128, p: u32) bool { - return pow5Factor(value) >= p; -} - -fn multipleOfPowerOf2(value: u128, p: u32) bool { - return (value & ((@as(u128, 1) << @as(u7, @intCast(p))) - 1)) == 0; -} - -fn computeInvPow5(i: u32) [4]u64 { - const base = (i + POW5_TABLE_SIZE - 1) / POW5_TABLE_SIZE; - const base2 = base * POW5_TABLE_SIZE; - const mul = &GENERIC_POW5_INV_SPLIT[base]; // 1 / 5^base2 - if (i == base2) { - return .{ mul[0] + 1, mul[1], mul[2], mul[3] }; - } else { - const offset = base2 - i; - const m = &GENERIC_POW5_TABLE[offset]; // 5^offset - const delta = pow5Bits(base2) - pow5Bits(i); - - const shift: u6 = @intCast(2 * (i % 32)); - const corr: u32 = @intCast(((POW5_INV_ERRORS[i / 32] >> shift) & 3) + 1); - return mul_128_256_shift(m, mul, delta, corr); - } -} - -fn computePow5(i: u32) [4]u64 { - const base = i / POW5_TABLE_SIZE; - const base2 = base * POW5_TABLE_SIZE; - const mul = &GENERIC_POW5_SPLIT[base]; - if (i == base2) { - return mul.*; - } else { - const offset = i - base2; - const m = &GENERIC_POW5_TABLE[offset]; - const delta = pow5Bits(i) - pow5Bits(base2); - - const shift: u6 = @intCast(2 * (i % 32)); - const corr: u32 = @intCast((POW5_ERRORS[i / 32] >> shift) & 3); - return mul_128_256_shift(m, mul, delta, corr); - } -} - -fn mulShift(m: u128, mul: *const [4]u64, j: u32) u128 { - std.debug.assert(j > 128); - const a: [2]u64 = .{ @truncate(m), @truncate(m >> 64) }; - const r = mul_128_256_shift(&a, mul, j, 0); - return (@as(u128, r[1]) << 64) | r[0]; -} - -fn mul_128_256_shift(a: *const [2]u64, b: *const [4]u64, shift: u32, corr: u32) [4]u64 { - std.debug.assert(shift > 0); - std.debug.assert(shift < 256); - - const b00 = @as(u128, a[0]) * b[0]; - const b01 = @as(u128, a[0]) * b[1]; - const b02 = @as(u128, a[0]) * b[2]; - const b03 = @as(u128, a[0]) * b[3]; - const b10 = @as(u128, a[1]) * b[0]; - const b11 = @as(u128, a[1]) * b[1]; - const b12 = @as(u128, a[1]) * b[2]; - const b13 = @as(u128, a[1]) * b[3]; - - const s0 = b00; - const s1 = b01 +% b10; - const c1: u128 = @intFromBool(s1 < b01); - const s2 = b02 +% b11; - const c2: u128 = @intFromBool(s2 < b02); - const s3 = b03 +% b12; - const c3: u128 = @intFromBool(s3 < b03); - - const p0 = s0 +% (s1 << 64); - const d0: u128 = @intFromBool(p0 < b00); - const q1 = s2 +% (s1 >> 64) +% (s3 << 64); - const d1: u128 = @intFromBool(q1 < s2); - const p1 = q1 +% (c1 << 64) +% d0; - const d2: u128 = @intFromBool(p1 < q1); - const p2 = b13 +% (s3 >> 64) +% c2 +% (c3 << 64) +% d1 +% d2; - - var r0: u128 = undefined; - var r1: u128 = undefined; - if (shift < 128) { - const cshift: u7 = @intCast(shift); - const sshift: u7 = @intCast(128 - shift); - r0 = corr +% ((p0 >> cshift) | (p1 << sshift)); - r1 = ((p1 >> cshift) | (p2 << sshift)) +% @intFromBool(r0 < corr); - } else if (shift == 128) { - r0 = corr +% p1; - r1 = p2 +% @intFromBool(r0 < corr); - } else { - const ashift: u7 = @intCast(shift - 128); - const sshift: u7 = @intCast(256 - shift); - r0 = corr +% ((p1 >> ashift) | (p2 << sshift)); - r1 = (p2 >> ashift) +% @intFromBool(r0 < corr); - } - - return .{ @truncate(r0), @truncate(r0 >> 64), @truncate(r1), @truncate(r1 >> 64) }; -} - -// zig fmt: off -// -// 4.5KiB of tables. - -const FLOAT_128_POW5_INV_BITCOUNT = 249; -const FLOAT_128_POW5_BITCOUNT = 249; -const POW5_TABLE_SIZE = 56; - -const GENERIC_POW5_TABLE: [POW5_TABLE_SIZE][2]u64 = .{ - .{ 1, 0 }, - .{ 5, 0 }, - .{ 25, 0 }, - .{ 125, 0 }, - .{ 625, 0 }, - .{ 3125, 0 }, - .{ 15625, 0 }, - .{ 78125, 0 }, - .{ 390625, 0 }, - .{ 1953125, 0 }, - .{ 9765625, 0 }, - .{ 48828125, 0 }, - .{ 244140625, 0 }, - .{ 1220703125, 0 }, - .{ 6103515625, 0 }, - .{ 30517578125, 0 }, - .{ 152587890625, 0 }, - .{ 762939453125, 0 }, - .{ 3814697265625, 0 }, - .{ 19073486328125, 0 }, - .{ 95367431640625, 0 }, - .{ 476837158203125, 0 }, - .{ 2384185791015625, 0 }, - .{ 11920928955078125, 0 }, - .{ 59604644775390625, 0 }, - .{ 298023223876953125, 0 }, - .{ 1490116119384765625, 0 }, - .{ 7450580596923828125, 0 }, - .{ 359414837200037393, 2 }, - .{ 1797074186000186965, 10 }, - .{ 8985370930000934825, 50 }, - .{ 8033366502585570893, 252 }, - .{ 3273344365508751233, 1262 }, - .{ 16366721827543756165, 6310 }, - .{ 8046632842880574361, 31554 }, - .{ 3339676066983768573, 157772 }, - .{ 16698380334918842865, 788860 }, - .{ 9704925379756007861, 3944304 }, - .{ 11631138751360936073, 19721522 }, - .{ 2815461535676025517, 98607613 }, - .{ 14077307678380127585, 493038065 }, - .{ 15046306170771983077, 2465190328 }, - .{ 1444554559021708921, 12325951644 }, - .{ 7222772795108544605, 61629758220 }, - .{ 17667119901833171409, 308148791101 }, - .{ 14548623214327650581, 1540743955509 }, - .{ 17402883850509598057, 7703719777548 }, - .{ 13227442957709783821, 38518598887744 }, - .{ 10796982567420264257, 192592994438723 }, - .{ 17091424689682218053, 962964972193617 }, - .{ 11670147153572883801, 4814824860968089 }, - .{ 3010503546735764157, 24074124304840448 }, - .{ 15052517733678820785, 120370621524202240 }, - .{ 1475612373555897461, 601853107621011204 }, - .{ 7378061867779487305, 3009265538105056020 }, - .{ 18443565265187884909, 15046327690525280101 }, -}; - -const GENERIC_POW5_SPLIT: [89][4]u64 = .{ - .{ 0, 0, 0, 72057594037927936 }, - .{ 0, 5206161169240293376, 4575641699882439235, 73468396926392969 }, - .{ 3360510775605221349, 6983200512169538081, 4325643253124434363, 74906821675075173 }, - .{ 11917660854915489451, 9652941469841108803, 946308467778435600, 76373409087490117 }, - .{ 1994853395185689235, 16102657350889591545, 6847013871814915412, 77868710555449746 }, - .{ 958415760277438274, 15059347134713823592, 7329070255463483331, 79393288266368765 }, - .{ 2065144883315240188, 7145278325844925976, 14718454754511147343, 80947715414629833 }, - .{ 8980391188862868935, 13709057401304208685, 8230434828742694591, 82532576417087045 }, - .{ 432148644612782575, 7960151582448466064, 12056089168559840552, 84148467132788711 }, - .{ 484109300864744403, 15010663910730448582, 16824949663447227068, 85795995087002057 }, - .{ 14793711725276144220, 16494403799991899904, 10145107106505865967, 87475779699624060 }, - .{ 15427548291869817042, 12330588654550505203, 13980791795114552342, 89188452518064298 }, - .{ 9979404135116626552, 13477446383271537499, 14459862802511591337, 90934657454687378 }, - .{ 12385121150303452775, 9097130814231585614, 6523855782339765207, 92715051028904201 }, - .{ 1822931022538209743, 16062974719797586441, 3619180286173516788, 94530302614003091 }, - .{ 12318611738248470829, 13330752208259324507, 10986694768744162601, 96381094688813589 }, - .{ 13684493829640282333, 7674802078297225834, 15208116197624593182, 98268123094297527 }, - .{ 5408877057066295332, 6470124174091971006, 15112713923117703147, 100192097295163851 }, - .{ 11407083166564425062, 18189998238742408185, 4337638702446708282, 102153740646605557 }, - .{ 4112405898036935485, 924624216579956435, 14251108172073737125, 104153790666259019 }, - .{ 16996739107011444789, 10015944118339042475, 2395188869672266257, 106192999311487969 }, - .{ 4588314690421337879, 5339991768263654604, 15441007590670620066, 108272133262096356 }, - .{ 2286159977890359825, 14329706763185060248, 5980012964059367667, 110391974208576409 }, - .{ 9654767503237031099, 11293544302844823188, 11739932712678287805, 112553319146000238 }, - .{ 11362964448496095896, 7990659682315657680, 251480263940996374, 114756980673665505 }, - .{ 1423410421096377129, 14274395557581462179, 16553482793602208894, 117003787300607788 }, - .{ 2070444190619093137, 11517140404712147401, 11657844572835578076, 119294583757094535 }, - .{ 7648316884775828921, 15264332483297977688, 247182277434709002, 121630231312217685 }, - .{ 17410896758132241352, 10923914482914417070, 13976383996795783649, 124011608097704390 }, - .{ 9542674537907272703, 3079432708831728956, 14235189590642919676, 126439609438067572 }, - .{ 10364666969937261816, 8464573184892924210, 12758646866025101190, 128915148187220428 }, - .{ 14720354822146013883, 11480204489231511423, 7449876034836187038, 131439155071681461 }, - .{ 1692907053653558553, 17835392458598425233, 1754856712536736598, 134012579040499057 }, - .{ 5620591334531458755, 11361776175667106627, 13350215315297937856, 136636387622027174 }, - .{ 17455759733928092601, 10362573084069962561, 11246018728801810510, 139311567287686283 }, - .{ 2465404073814044982, 17694822665274381860, 1509954037718722697, 142039123822846312 }, - .{ 2152236053329638369, 11202280800589637091, 16388426812920420176, 72410041352485523 }, - .{ 17319024055671609028, 10944982848661280484, 2457150158022562661, 73827744744583080 }, - .{ 17511219308535248024, 5122059497846768077, 2089605804219668451, 75273205100637900 }, - .{ 10082673333144031533, 14429008783411894887, 12842832230171903890, 76746965869337783 }, - .{ 16196653406315961184, 10260180891682904501, 10537411930446752461, 78249581139456266 }, - .{ 15084422041749743389, 234835370106753111, 16662517110286225617, 79781615848172976 }, - .{ 8199644021067702606, 3787318116274991885, 7438130039325743106, 81343645993472659 }, - .{ 12039493937039359765, 9773822153580393709, 5945428874398357806, 82936258850702722 }, - .{ 984543865091303961, 7975107621689454830, 6556665988501773347, 84560053193370726 }, - .{ 9633317878125234244, 16099592426808915028, 9706674539190598200, 86215639518264828 }, - .{ 6860695058870476186, 4471839111886709592, 7828342285492709568, 87903640274981819 }, - .{ 14583324717644598331, 4496120889473451238, 5290040788305728466, 89624690099949049 }, - .{ 18093669366515003715, 12879506572606942994, 18005739787089675377, 91379436055028227 }, - .{ 17997493966862379937, 14646222655265145582, 10265023312844161858, 93168537870790806 }, - .{ 12283848109039722318, 11290258077250314935, 9878160025624946825, 94992668194556404 }, - .{ 8087752761883078164, 5262596608437575693, 11093553063763274413, 96852512843287537 }, - .{ 15027787746776840781, 12250273651168257752, 9290470558712181914, 98748771061435726 }, - .{ 15003915578366724489, 2937334162439764327, 5404085603526796602, 100682155783835929 }, - .{ 5225610465224746757, 14932114897406142027, 2774647558180708010, 102653393903748137 }, - .{ 17112957703385190360, 12069082008339002412, 3901112447086388439, 104663226546146909 }, - .{ 4062324464323300238, 3992768146772240329, 15757196565593695724, 106712409346361594 }, - .{ 5525364615810306701, 11855206026704935156, 11344868740897365300, 108801712734172003 }, - .{ 9274143661888462646, 4478365862348432381, 18010077872551661771, 110931922223466333 }, - .{ 12604141221930060148, 8930937759942591500, 9382183116147201338, 113103838707570263 }, - .{ 14513929377491886653, 1410646149696279084, 587092196850797612, 115318278760358235 }, - .{ 2226851524999454362, 7717102471110805679, 7187441550995571734, 117576074943260147 }, - .{ 5527526061344932763, 2347100676188369132, 16976241418824030445, 119878076118278875 }, - .{ 6088479778147221611, 17669593130014777580, 10991124207197663546, 122225147767136307 }, - .{ 11107734086759692041, 3391795220306863431, 17233960908859089158, 124618172316667879 }, - .{ 7913172514655155198, 17726879005381242552, 641069866244011540, 127058049470587962 }, - .{ 12596991768458713949, 15714785522479904446, 6035972567136116512, 129545696547750811 }, - .{ 16901996933781815980, 4275085211437148707, 14091642539965169063, 132082048827034281 }, - .{ 7524574627987869240, 15661204384239316051, 2444526454225712267, 134668059898975949 }, - .{ 8199251625090479942, 6803282222165044067, 16064817666437851504, 137304702024293857 }, - .{ 4453256673338111920, 15269922543084434181, 3139961729834750852, 139992966499426682 }, - .{ 15841763546372731299, 3013174075437671812, 4383755396295695606, 142733864029230733 }, - .{ 9771896230907310329, 4900659362437687569, 12386126719044266361, 72764212553486967 }, - .{ 9420455527449565190, 1859606122611023693, 6555040298902684281, 74188850200884818 }, - .{ 5146105983135678095, 2287300449992174951, 4325371679080264751, 75641380576797959 }, - .{ 11019359372592553360, 8422686425957443718, 7175176077944048210, 77122349788024458 }, - .{ 11005742969399620716, 4132174559240043701, 9372258443096612118, 78632314633490790 }, - .{ 8887589641394725840, 8029899502466543662, 14582206497241572853, 80171842813591127 }, - .{ 360247523705545899, 12568341805293354211, 14653258284762517866, 81741513143625247 }, - .{ 12314272731984275834, 4740745023227177044, 6141631472368337539, 83341915771415304 }, - .{ 441052047733984759, 7940090120939869826, 11750200619921094248, 84973652399183278 }, - .{ 3436657868127012749, 9187006432149937667, 16389726097323041290, 86637336509772529 }, - .{ 13490220260784534044, 15339072891382896702, 8846102360835316895, 88333593597298497 }, - .{ 4125672032094859833, 158347675704003277, 10592598512749774447, 90063061402315272 }, - .{ 12189928252974395775, 2386931199439295891, 7009030566469913276, 91826390151586454 }, - .{ 9256479608339282969, 2844900158963599229, 11148388908923225596, 93624242802550437 }, - .{ 11584393507658707408, 2863659090805147914, 9873421561981063551, 95457295292572042 }, - .{ 13984297296943171390, 1931468383973130608, 12905719743235082319, 97326236793074198 }, - .{ 5837045222254987499, 10213498696735864176, 14893951506257020749, 99231769968645227 }, -}; - -// Unfortunately, the results are sometimes off by one or two. We use an additional -// lookup table to store those cases and adjust the result. -const POW5_ERRORS: [156]u64 = .{ - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x9555596400000000, - 0x65a6569525565555, 0x4415551445449655, 0x5105015504144541, 0x65a69969a6965964, - 0x5054955969959656, 0x5105154515554145, 0x4055511051591555, 0x5500514455550115, - 0x0041140014145515, 0x1005440545511051, 0x0014405450411004, 0x0414440010500000, - 0x0044000440010040, 0x5551155000004001, 0x4554555454544114, 0x5150045544005441, - 0x0001111400054501, 0x6550955555554554, 0x1504159645559559, 0x4105055141454545, - 0x1411541410405454, 0x0415555044545555, 0x0014154115405550, 0x1540055040411445, - 0x0000000500000000, 0x5644000000000000, 0x1155555591596555, 0x0410440054569565, - 0x5145100010010005, 0x0555041405500150, 0x4141450455140450, 0x0000000144000140, - 0x5114004001105410, 0x4444100404005504, 0x0414014410001015, 0x5145055155555015, - 0x0141041444445540, 0x0000100451541414, 0x4105041104155550, 0x0500501150451145, - 0x1001050000004114, 0x5551504400141045, 0x5110545410151454, 0x0100001400004040, - 0x5040010111040000, 0x0140000150541100, 0x4400140400104110, 0x5011014405545004, - 0x0000000044155440, 0x0000000010000000, 0x1100401444440001, 0x0040401010055111, - 0x5155155551405454, 0x0444440015514411, 0x0054505054014101, 0x0451015441115511, - 0x1541411401140551, 0x4155104514445110, 0x4141145450145515, 0x5451445055155050, - 0x4400515554110054, 0x5111145104501151, 0x565a655455500501, 0x5565555555525955, - 0x0550511500405695, 0x4415504051054544, 0x6555595965555554, 0x0100915915555655, - 0x5540001510001001, 0x5450051414000544, 0x1405010555555551, 0x5555515555644155, - 0x5555055595496555, 0x5451045004415000, 0x5450510144040144, 0x5554155555556455, - 0x5051555495415555, 0x5555554555555545, 0x0000000010005455, 0x4000005000040000, - 0x5565555555555954, 0x5554559555555505, 0x9645545495552555, 0x4000400055955564, - 0x0040000000000001, 0x4004100100000000, 0x5540040440000411, 0x4565555955545644, - 0x1140659549651556, 0x0100000410010000, 0x5555515400004001, 0x5955545555155255, - 0x5151055545505556, 0x5051454510554515, 0x0501500050415554, 0x5044154005441005, - 0x1455445450550455, 0x0010144055144545, 0x0000401100000004, 0x1050145050000010, - 0x0415004554011540, 0x1000510100151150, 0x0100040400001144, 0x0000000000000000, - 0x0550004400000100, 0x0151145041451151, 0x0000400400005450, 0x0000100044010004, - 0x0100054100050040, 0x0504400005410010, 0x4011410445500105, 0x0000404000144411, - 0x0101504404500000, 0x0000005044400400, 0x0000000014000100, 0x0404440414000000, - 0x5554100410000140, 0x4555455544505555, 0x5454105055455455, 0x0115454155454015, - 0x4404110000045100, 0x4400001100101501, 0x6596955956966a94, 0x0040655955665965, - 0x5554144400100155, 0xa549495401011041, 0x5596555565955555, 0x5569965959549555, - 0x969565a655555456, 0x0000001000000000, 0x0000000040000140, 0x0000040100000000, - 0x1415454400000000, 0x5410415411454114, 0x0400040104000154, 0x0504045000000411, - 0x0000001000000010, 0x5554000000001040, 0x5549155551556595, 0x1455541055515555, - 0x0510555454554541, 0x9555555555540455, 0x6455456555556465, 0x4524565555654514, - 0x5554655255559545, 0x9555455441155556, 0x0000000051515555, 0x0010005040000550, - 0x5044044040000000, 0x1045040440010500, 0x0000400000040000, 0x0000000000000000, -}; - -const GENERIC_POW5_INV_SPLIT: [89][4]u64 = .{ - .{ 0, 0, 0, 144115188075855872 }, - .{ 1573859546583440065, 2691002611772552616, 6763753280790178510, 141347765182270746 }, - .{ 12960290449513840412, 12345512957918226762, 18057899791198622765, 138633484706040742 }, - .{ 7615871757716765416, 9507132263365501332, 4879801712092008245, 135971326161092377 }, - .{ 7869961150745287587, 5804035291554591636, 8883897266325833928, 133360288657597085 }, - .{ 2942118023529634767, 15128191429820565086, 10638459445243230718, 130799390525667397 }, - .{ 14188759758411913794, 5362791266439207815, 8068821289119264054, 128287668946279217 }, - .{ 7183196927902545212, 1952291723540117099, 12075928209936341512, 125824179589281448 }, - .{ 5672588001402349748, 17892323620748423487, 9874578446960390364, 123407996258356868 }, - .{ 4442590541217566325, 4558254706293456445, 10343828952663182727, 121038210542800766 }, - .{ 3005560928406962566, 2082271027139057888, 13961184524927245081, 118713931475986426 }, - .{ 13299058168408384786, 17834349496131278595, 9029906103900731664, 116434285200389047 }, - .{ 5414878118283973035, 13079825470227392078, 17897304791683760280, 114198414639042157 }, - .{ 14609755883382484834, 14991702445765844156, 3269802549772755411, 112005479173303009 }, - .{ 15967774957605076027, 2511532636717499923, 16221038267832563171, 109854654326805788 }, - .{ 9269330061621627145, 3332501053426257392, 16223281189403734630, 107745131455483836 }, - .{ 16739559299223642282, 1873986623300664530, 6546709159471442872, 105676117443544318 }, - .{ 17116435360051202055, 1359075105581853924, 2038341371621886470, 103646834405281051 }, - .{ 17144715798009627550, 3201623802661132408, 9757551605154622431, 101656519392613377 }, - .{ 17580479792687825857, 6546633380567327312, 15099972427870912398, 99704424108241124 }, - .{ 9726477118325522902, 14578369026754005435, 11728055595254428803, 97789814624307808 }, - .{ 134593949518343635, 5715151379816901985, 1660163707976377376, 95911971106466306 }, - .{ 5515914027713859358, 7124354893273815720, 5548463282858794077, 94070187543243255 }, - .{ 6188403395862945512, 5681264392632320838, 15417410852121406654, 92263771480600430 }, - .{ 15908890877468271457, 10398888261125597540, 4817794962769172309, 90492043761593298 }, - .{ 1413077535082201005, 12675058125384151580, 7731426132303759597, 88754338271028867 }, - .{ 1486733163972670293, 11369385300195092554, 11610016711694864110, 87050001685026843 }, - .{ 8788596583757589684, 3978580923851924802, 9255162428306775812, 85378393225389919 }, - .{ 7203518319660962120, 15044736224407683725, 2488132019818199792, 83738884418690858 }, - .{ 4004175967662388707, 18236988667757575407, 15613100370957482671, 82130858859985791 }, - .{ 18371903370586036463, 53497579022921640, 16465963977267203307, 80553711981064899 }, - .{ 10170778323887491315, 1999668801648976001, 10209763593579456445, 79006850823153334 }, - .{ 17108131712433974546, 16825784443029944237, 2078700786753338945, 77489693813976938 }, - .{ 17221789422665858532, 12145427517550446164, 5391414622238668005, 76001670549108934 }, - .{ 4859588996898795878, 1715798948121313204, 3950858167455137171, 74542221577515387 }, - .{ 13513469241795711526, 631367850494860526, 10517278915021816160, 73110798191218799 }, - .{ 11757513142672073111, 2581974932255022228, 17498959383193606459, 143413724438001539 }, - .{ 14524355192525042817, 5640643347559376447, 1309659274756813016, 140659771648132296 }, - .{ 2765095348461978538, 11021111021896007722, 3224303603779962366, 137958702611185230 }, - .{ 12373410389187981037, 13679193545685856195, 11644609038462631561, 135309501808182158 }, - .{ 12813176257562780151, 3754199046160268020, 9954691079802960722, 132711173221007413 }, - .{ 17557452279667723458, 3237799193992485824, 17893947919029030695, 130162739957935629 }, - .{ 14634200999559435155, 4123869946105211004, 6955301747350769239, 127663243886350468 }, - .{ 2185352760627740240, 2864813346878886844, 13049218671329690184, 125211745272516185 }, - .{ 6143438674322183002, 10464733336980678750, 6982925169933978309, 122807322428266620 }, - .{ 1099509117817174576, 10202656147550524081, 754997032816608484, 120449071364478757 }, - .{ 2410631293559367023, 17407273750261453804, 15307291918933463037, 118136105451200587 }, - .{ 12224968375134586697, 1664436604907828062, 11506086230137787358, 115867555084305488 }, - .{ 3495926216898000888, 18392536965197424288, 10992889188570643156, 113642567358547782 }, - .{ 8744506286256259680, 3966568369496879937, 18342264969761820037, 111460305746896569 }, - .{ 7689600520560455039, 5254331190877624630, 9628558080573245556, 109319949786027263 }, - .{ 11862637625618819436, 3456120362318976488, 14690471063106001082, 107220694767852583 }, - .{ 5697330450030126444, 12424082405392918899, 358204170751754904, 105161751436977040 }, - .{ 11257457505097373622, 15373192700214208870, 671619062372033814, 103142345693961148 }, - .{ 16850355018477166700, 1913910419361963966, 4550257919755970531, 101161718304283822 }, - .{ 9670835567561997011, 10584031339132130638, 3060560222974851757, 99219124612893520 }, - .{ 7698686577353054710, 11689292838639130817, 11806331021588878241, 97313834264240819 }, - .{ 12233569599615692137, 3347791226108469959, 10333904326094451110, 95445130927687169 }, - .{ 13049400362825383933, 17142621313007799680, 3790542585289224168, 93612312028186576 }, - .{ 12430457242474442072, 5625077542189557960, 14765055286236672238, 91814688482138969 }, - .{ 4759444137752473128, 2230562561567025078, 4954443037339580076, 90051584438315940 }, - .{ 7246913525170274758, 8910297835195760709, 4015904029508858381, 88322337023761438 }, - .{ 12854430245836432067, 8135139748065431455, 11548083631386317976, 86626296094571907 }, - .{ 4848827254502687803, 4789491250196085625, 3988192420450664125, 84962823991462151 }, - .{ 7435538409611286684, 904061756819742353, 14598026519493048444, 83331295300025028 }, - .{ 11042616160352530997, 8948390828345326218, 10052651191118271927, 81731096615594853 }, - .{ 11059348291563778943, 11696515766184685544, 3783210511290897367, 80161626312626082 }, - .{ 7020010856491885826, 5025093219346041680, 8960210401638911765, 78622294318500592 }, - .{ 17732844474490699984, 7820866704994446502, 6088373186798844243, 77112521891678506 }, - .{ 688278527545590501, 3045610706602776618, 8684243536999567610, 75631741404109150 }, - .{ 2734573255120657297, 3903146411440697663, 9470794821691856713, 74179396127820347 }, - .{ 15996457521023071259, 4776627823451271680, 12394856457265744744, 72754940025605801 }, - .{ 13492065758834518331, 7390517611012222399, 1630485387832860230, 142715675091463768 }, - .{ 13665021627282055864, 9897834675523659302, 17907668136755296849, 139975126841173266 }, - .{ 9603773719399446181, 10771916301484339398, 10672699855989487527, 137287204938390542 }, - .{ 3630218541553511265, 8139010004241080614, 2876479648932814543, 134650898807055963 }, - .{ 8318835909686377084, 9525369258927993371, 2796120270400437057, 132065217277054270 }, - .{ 11190003059043290163, 12424345635599592110, 12539346395388933763, 129529188211565064 }, - .{ 8701968833973242276, 820569587086330727, 2315591597351480110, 127041858141569228 }, - .{ 5115113890115690487, 16906305245394587826, 9899749468931071388, 124602291907373862 }, - .{ 15543535488939245974, 10945189844466391399, 3553863472349432246, 122209572307020975 }, - .{ 7709257252608325038, 1191832167690640880, 15077137020234258537, 119862799751447719 }, - .{ 7541333244210021737, 9790054727902174575, 5160944773155322014, 117561091926268545 }, - .{ 12297384708782857832, 1281328873123467374, 4827925254630475769, 115303583460052092 }, - .{ 13243237906232367265, 15873887428139547641, 3607993172301799599, 113089425598968120 }, - .{ 11384616453739611114, 15184114243769211033, 13148448124803481057, 110917785887682141 }, - .{ 17727970963596660683, 1196965221832671990, 14537830463956404138, 108787847856377790 }, - .{ 17241367586707330931, 8880584684128262874, 11173506540726547818, 106698810713789254 }, - .{ 7184427196661305643, 14332510582433188173, 14230167953789677901, 104649889046128358 }, -}; - -const POW5_INV_ERRORS: [154]u64 = .{ - 0x1144155514145504, 0x0000541555401141, 0x0000000000000000, 0x0154454000000000, - 0x4114105515544440, 0x0001001111500415, 0x4041411410011000, 0x5550114515155014, - 0x1404100041554551, 0x0515000450404410, 0x5054544401140004, 0x5155501005555105, - 0x1144141000105515, 0x0541500000500000, 0x1104105540444140, 0x4000015055514110, - 0x0054010450004005, 0x4155515404100005, 0x5155145045155555, 0x1511555515440558, - 0x5558544555515555, 0x0000000000000010, 0x5004000000000050, 0x1415510100000010, - 0x4545555444514500, 0x5155151555555551, 0x1441540144044554, 0x5150104045544400, - 0x5450545401444040, 0x5554455045501400, 0x4655155555555145, 0x1000010055455055, - 0x1000004000055004, 0x4455405104000005, 0x4500114504150545, 0x0000000014000000, - 0x5450000000000000, 0x5514551511445555, 0x4111501040555451, 0x4515445500054444, - 0x5101500104100441, 0x1545115155545055, 0x0000000000000000, 0x1554000000100000, - 0x5555545595551555, 0x5555051851455955, 0x5555555555555559, 0x0000400011001555, - 0x0000004400040000, 0x5455511555554554, 0x5614555544115445, 0x6455156145555155, - 0x5455855455415455, 0x5515555144555545, 0x0114400000145155, 0x0000051000450511, - 0x4455154554445100, 0x4554150141544455, 0x65955555559a5965, 0x5555555854559559, - 0x9569654559616595, 0x1040044040005565, 0x1010010500011044, 0x1554015545154540, - 0x4440555401545441, 0x1014441450550105, 0x4545400410504145, 0x5015111541040151, - 0x5145051154000410, 0x1040001044545044, 0x4001400000151410, 0x0540000044040000, - 0x0510555454411544, 0x0400054054141550, 0x1001041145001100, 0x0000000140000000, - 0x0000000014100000, 0x1544005454000140, 0x4050055505445145, 0x0011511104504155, - 0x5505544415045055, 0x1155154445515554, 0x0000000000004555, 0x0000000000000000, - 0x5101010510400004, 0x1514045044440400, 0x5515519555515555, 0x4554545441555545, - 0x1551055955551515, 0x0150000011505515, 0x0044005040400000, 0x0004001004010050, - 0x0000051004450414, 0x0114001101001144, 0x0401000001000001, 0x4500010001000401, - 0x0004100000005000, 0x0105000441101100, 0x0455455550454540, 0x5404050144105505, - 0x4101510540555455, 0x1055541411451555, 0x5451445110115505, 0x1154110010101545, - 0x1145140450054055, 0x5555565415551554, 0x1550559555555555, 0x5555541545045141, - 0x4555455450500100, 0x5510454545554555, 0x1510140115045455, 0x1001050040111510, - 0x5555454555555504, 0x9954155545515554, 0x6596656555555555, 0x0140410051555559, - 0x0011104010001544, 0x965669659a680501, 0x5655a55955556955, 0x4015111014404514, - 0x1414155554505145, 0x0540040011051404, 0x1010000000015005, 0x0010054050004410, - 0x5041104014000100, 0x4440010500100001, 0x1155510504545554, 0x0450151545115541, - 0x4000100400110440, 0x1004440010514440, 0x0000115050450000, 0x0545404455541500, - 0x1051051555505101, 0x5505144554544144, 0x4550545555515550, 0x0015400450045445, - 0x4514155400554415, 0x4555055051050151, 0x1511441450001014, 0x4544554510404414, - 0x4115115545545450, 0x5500541555551555, 0x5550010544155015, 0x0144414045545500, - 0x4154050001050150, 0x5550511111000145, 0x1114504055000151, 0x5104041101451040, - 0x0010501401051441, 0x0010501450504401, 0x4554585440044444, 0x5155555951450455, - 0x0040000400105555, 0x0000000000000001, -}; - -// zig fmt: on - -fn check(comptime T: type, value: T, comptime expected: []const u8) !void { - const I = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } }); - - var buf: [6000]u8 = undefined; - const value_bits: I = @bitCast(value); - const s = try format(&buf, value, .{}); - try std.testing.expectEqualStrings(expected, s); - - if (@bitSizeOf(T) != 80) { - const o = try std.fmt.parseFloat(T, s); - const o_bits: I = @bitCast(o); - - if (std.math.isNan(value)) { - try std.testing.expect(std.math.isNan(o)); - } else { - try std.testing.expectEqual(value_bits, o_bits); - } - } -} - -test "format f32" { - try check(f32, 0.0, "0e0"); - try check(f32, -0.0, "-0e0"); - try check(f32, 1.0, "1e0"); - try check(f32, -1.0, "-1e0"); - try check(f32, std.math.nan(f32), "nan"); - try check(f32, std.math.inf(f32), "inf"); - try check(f32, -std.math.inf(f32), "-inf"); - try check(f32, 1.1754944e-38, "1.1754944e-38"); - try check(f32, @bitCast(@as(u32, 0x7f7fffff)), "3.4028235e38"); - try check(f32, @bitCast(@as(u32, 1)), "1e-45"); - try check(f32, 3.355445E7, "3.355445e7"); - try check(f32, 8.999999e9, "9e9"); - try check(f32, 3.4366717e10, "3.436672e10"); - try check(f32, 3.0540412e5, "3.0540412e5"); - try check(f32, 8.0990312e3, "8.0990312e3"); - try check(f32, 2.4414062e-4, "2.4414062e-4"); - try check(f32, 2.4414062e-3, "2.4414062e-3"); - try check(f32, 4.3945312e-3, "4.3945312e-3"); - try check(f32, 6.3476562e-3, "6.3476562e-3"); - try check(f32, 4.7223665e21, "4.7223665e21"); - try check(f32, 8388608.0, "8.388608e6"); - try check(f32, 1.6777216e7, "1.6777216e7"); - try check(f32, 3.3554436e7, "3.3554436e7"); - try check(f32, 6.7131496e7, "6.7131496e7"); - try check(f32, 1.9310392e-38, "1.9310392e-38"); - try check(f32, -2.47e-43, "-2.47e-43"); - try check(f32, 1.993244e-38, "1.993244e-38"); - try check(f32, 4103.9003, "4.1039004e3"); - try check(f32, 5.3399997e9, "5.3399997e9"); - try check(f32, 6.0898e-39, "6.0898e-39"); - try check(f32, 0.0010310042, "1.0310042e-3"); - try check(f32, 2.8823261e17, "2.882326e17"); - try check(f32, 7.038531e-26, "7.038531e-26"); - try check(f32, 9.2234038e17, "9.223404e17"); - try check(f32, 6.7108872e7, "6.710887e7"); - try check(f32, 1.0e-44, "1e-44"); - try check(f32, 2.816025e14, "2.816025e14"); - try check(f32, 9.223372e18, "9.223372e18"); - try check(f32, 1.5846085e29, "1.5846086e29"); - try check(f32, 1.1811161e19, "1.1811161e19"); - try check(f32, 5.368709e18, "5.368709e18"); - try check(f32, 4.6143165e18, "4.6143166e18"); - try check(f32, 0.007812537, "7.812537e-3"); - try check(f32, 1.4e-45, "1e-45"); - try check(f32, 1.18697724e20, "1.18697725e20"); - try check(f32, 1.00014165e-36, "1.00014165e-36"); - try check(f32, 200.0, "2e2"); - try check(f32, 3.3554432e7, "3.3554432e7"); - - try check(f32, 1.0, "1e0"); - try check(f32, 1.2, "1.2e0"); - try check(f32, 1.23, "1.23e0"); - try check(f32, 1.234, "1.234e0"); - try check(f32, 1.2345, "1.2345e0"); - try check(f32, 1.23456, "1.23456e0"); - try check(f32, 1.234567, "1.234567e0"); - try check(f32, 1.2345678, "1.2345678e0"); - try check(f32, 1.23456735e-36, "1.23456735e-36"); -} - -test "format f64" { - try check(f64, 0.0, "0e0"); - try check(f64, -0.0, "-0e0"); - try check(f64, 1.0, "1e0"); - try check(f64, -1.0, "-1e0"); - try check(f64, std.math.nan(f64), "nan"); - try check(f64, std.math.inf(f64), "inf"); - try check(f64, -std.math.inf(f64), "-inf"); - try check(f64, 2.2250738585072014e-308, "2.2250738585072014e-308"); - try check(f64, @bitCast(@as(u64, 0x7fefffffffffffff)), "1.7976931348623157e308"); - try check(f64, @bitCast(@as(u64, 1)), "5e-324"); - try check(f64, 2.98023223876953125e-8, "2.9802322387695312e-8"); - try check(f64, -2.109808898695963e16, "-2.109808898695963e16"); - try check(f64, 4.940656e-318, "4.940656e-318"); - try check(f64, 1.18575755e-316, "1.18575755e-316"); - try check(f64, 2.989102097996e-312, "2.989102097996e-312"); - try check(f64, 9.0608011534336e15, "9.0608011534336e15"); - try check(f64, 4.708356024711512e18, "4.708356024711512e18"); - try check(f64, 9.409340012568248e18, "9.409340012568248e18"); - try check(f64, 1.2345678, "1.2345678e0"); - try check(f64, @bitCast(@as(u64, 0x4830f0cf064dd592)), "5.764607523034235e39"); - try check(f64, @bitCast(@as(u64, 0x4840f0cf064dd592)), "1.152921504606847e40"); - try check(f64, @bitCast(@as(u64, 0x4850f0cf064dd592)), "2.305843009213694e40"); - - try check(f64, 1, "1e0"); - try check(f64, 1.2, "1.2e0"); - try check(f64, 1.23, "1.23e0"); - try check(f64, 1.234, "1.234e0"); - try check(f64, 1.2345, "1.2345e0"); - try check(f64, 1.23456, "1.23456e0"); - try check(f64, 1.234567, "1.234567e0"); - try check(f64, 1.2345678, "1.2345678e0"); - try check(f64, 1.23456789, "1.23456789e0"); - try check(f64, 1.234567895, "1.234567895e0"); - try check(f64, 1.2345678901, "1.2345678901e0"); - try check(f64, 1.23456789012, "1.23456789012e0"); - try check(f64, 1.234567890123, "1.234567890123e0"); - try check(f64, 1.2345678901234, "1.2345678901234e0"); - try check(f64, 1.23456789012345, "1.23456789012345e0"); - try check(f64, 1.234567890123456, "1.234567890123456e0"); - try check(f64, 1.2345678901234567, "1.2345678901234567e0"); - - try check(f64, 4.294967294, "4.294967294e0"); - try check(f64, 4.294967295, "4.294967295e0"); - try check(f64, 4.294967296, "4.294967296e0"); - try check(f64, 4.294967297, "4.294967297e0"); - try check(f64, 4.294967298, "4.294967298e0"); -} - -test "format f80" { - try check(f80, 0.0, "0e0"); - try check(f80, -0.0, "-0e0"); - try check(f80, 1.0, "1e0"); - try check(f80, -1.0, "-1e0"); - try check(f80, std.math.nan(f80), "nan"); - try check(f80, std.math.inf(f80), "inf"); - try check(f80, -std.math.inf(f80), "-inf"); - - try check(f80, 2.2250738585072014e-308, "2.2250738585072014e-308"); - try check(f80, 2.98023223876953125e-8, "2.98023223876953125e-8"); - try check(f80, -2.109808898695963e16, "-2.109808898695963e16"); - try check(f80, 4.940656e-318, "4.940656e-318"); - try check(f80, 1.18575755e-316, "1.18575755e-316"); - try check(f80, 2.989102097996e-312, "2.989102097996e-312"); - try check(f80, 9.0608011534336e15, "9.0608011534336e15"); - try check(f80, 4.708356024711512e18, "4.708356024711512e18"); - try check(f80, 9.409340012568248e18, "9.409340012568248e18"); - try check(f80, 1.2345678, "1.2345678e0"); -} - -test "format f128" { - try check(f128, 0.0, "0e0"); - try check(f128, -0.0, "-0e0"); - try check(f128, 1.0, "1e0"); - try check(f128, -1.0, "-1e0"); - try check(f128, std.math.nan(f128), "nan"); - try check(f128, std.math.inf(f128), "inf"); - try check(f128, -std.math.inf(f128), "-inf"); - - try check(f128, 2.2250738585072014e-308, "2.2250738585072014e-308"); - try check(f128, 2.98023223876953125e-8, "2.98023223876953125e-8"); - try check(f128, -2.109808898695963e16, "-2.109808898695963e16"); - try check(f128, 4.940656e-318, "4.940656e-318"); - try check(f128, 1.18575755e-316, "1.18575755e-316"); - try check(f128, 2.989102097996e-312, "2.989102097996e-312"); - try check(f128, 9.0608011534336e15, "9.0608011534336e15"); - try check(f128, 4.708356024711512e18, "4.708356024711512e18"); - try check(f128, 9.409340012568248e18, "9.409340012568248e18"); - try check(f128, 1.2345678, "1.2345678e0"); -} - -test "format float to decimal with zero precision" { - try expectFmt("5", "{d:.0}", .{5}); - try expectFmt("6", "{d:.0}", .{6}); - try expectFmt("7", "{d:.0}", .{7}); - try expectFmt("8", "{d:.0}", .{8}); -}