Created
September 28, 2024 18:46
-
-
Save kassane/568786d4a177cf85aa7c7299d2fb6915 to your computer and use it in GitHub Desktop.
Dragonbox: float format (based on https://github.com/jk-jeon/dragonbox)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const math = std.math; | |
const assert = std.debug.assert; | |
const testing = std.testing; | |
pub const Policy = struct { | |
pub const SignPolicy = enum { | |
return_sign, | |
ignore_sign, | |
}; | |
pub const TrailingZeroPolicy = enum { | |
remove_trailing_zeros, | |
return_all_digits, | |
}; | |
pub const BinaryRoundingPolicy = enum { | |
nearest_to_even, | |
nearest_to_odd, | |
toward_zero, | |
toward_infinity, | |
}; | |
pub const DecimalRoundingPolicy = enum { | |
do_not_care, | |
round_down, | |
round_up, | |
round_to_even, | |
round_to_odd, | |
}; | |
}; | |
pub const FloatingPointToCharsResult = struct { | |
ptr: [*]const u8, | |
len: usize, | |
}; | |
fn floorLog10(x: anytype) @TypeOf(x) { | |
return @floor(@log10(x)); | |
} | |
fn ceilLog10(x: anytype) @TypeOf(x) { | |
return @ceil(@log10(x)); | |
} | |
fn getCache(k: i32) struct { significand: u64, exponent: i32 } { | |
// Implement cache lookup | |
_ = k; | |
return .{ .significand = 0, .exponent = 0 }; | |
} | |
pub fn toChars(comptime T: type, value: T, buffer: []u8) FloatingPointToCharsResult { | |
const float_info = @typeInfo(T).float; | |
const bits = @typeInfo(T).float.bits; | |
const UintT = std.meta.Int(.unsigned, bits); | |
if (value == 0) { | |
buffer[0] = '0'; | |
return FloatingPointToCharsResult{ .ptr = buffer.ptr, .len = 1 }; | |
} | |
// FIXME | |
const bits_value = @typeInfo(UintT).int.bits; | |
const exponent_bits = float_info.bits; | |
const significand_bits = float_info.bits; | |
const exponent_bias = (1 << (exponent_bits - 1)) - 1; | |
const significand_mask = (1 << significand_bits) - 1; | |
var significand = bits_value & significand_mask; | |
var exponent = @as(i32, (bits_value >> significand_bits) & ((1 << exponent_bits) - 1)); | |
if (exponent == 0) { | |
// Subnormal number | |
exponent = 1 - exponent_bias; | |
} else { | |
// Normal number | |
significand |= (1 << significand_bits); | |
exponent -= exponent_bias; | |
} | |
// Implement the main dragonbox algorithm here | |
// This is a simplified version and doesn't cover all cases | |
var decimal_exponent = @as(i32, floorLog10(@as(f64, significand)) + @as(f64, exponent) * std.math.log10(2)); | |
const power_of_10 = std.math.pow(f64, 10, @as(f64, -decimal_exponent)); | |
var scaled_value = @as(f64, significand) * std.math.pow(f64, 2, @as(f64, exponent)) * power_of_10; | |
var idx: usize = 0; | |
while (scaled_value > 0 and idx < buffer.len) : (idx += 1) { | |
const digit = @as(u8, scaled_value); | |
buffer[idx] = '0' + digit; | |
scaled_value = (scaled_value - @as(f64, digit)) * 10; | |
} | |
// Add decimal point if necessary | |
if (decimal_exponent <= 0) { | |
std.mem.copy(u8, buffer[1..], buffer[0..idx]); | |
buffer[0] = '0'; | |
buffer[1] = '.'; | |
idx += 2; | |
while (decimal_exponent < 0) : (decimal_exponent += 1) { | |
buffer[idx] = '0'; | |
idx += 1; | |
} | |
} else if (decimal_exponent < idx) { | |
std.mem.copyBackwards(u8, buffer[decimal_exponent + 1 .. idx + 1], buffer[decimal_exponent..idx]); | |
buffer[decimal_exponent] = '.'; | |
idx += 1; | |
} | |
return FloatingPointToCharsResult{ .ptr = buffer.ptr, .len = idx }; | |
} | |
test "floorLog10" { | |
try testing.expectEqual(@as(f64, 2), floorLog10(@as(f64, 100.0))); | |
try testing.expectEqual(@as(f64, 2), floorLog10(@as(f64, 999.9))); | |
try testing.expectEqual(@as(f64, 3), floorLog10(@as(f64, 1000.0))); | |
} | |
test "ceilLog10" { | |
try testing.expectEqual(@as(f64, 2), ceilLog10(@as(f64, 99.9))); | |
try testing.expectEqual(@as(f64, 2), ceilLog10(@as(f64, 100.0))); | |
try testing.expectEqual(@as(f64, 3), ceilLog10(@as(f64, 100.1))); | |
} | |
// test "toChars" { | |
// var buffer: [30]u8 = undefined; | |
// | |
// { | |
// const result = toChars(f64, 3.14159, &buffer); | |
// try testing.expectEqualStrings("3.14159", buffer[0..result.len]); | |
// } | |
// | |
// { | |
// const result = toChars(f64, 0.0, &buffer); | |
// try testing.expectEqualStrings("0", buffer[0..result.len]); | |
// } | |
// | |
// { | |
// const result = toChars(f64, 1000.0, &buffer); | |
// try testing.expectEqualStrings("1000", buffer[0..result.len]); | |
// } | |
// | |
// { | |
// const result = toChars(f64, 0.001, &buffer); | |
// try testing.expectEqualStrings("0.001", buffer[0..result.len]); | |
// } | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment