Skip to content

Instantly share code, notes, and snippets.

@kassane
Created September 28, 2024 18:46
Show Gist options
  • Save kassane/568786d4a177cf85aa7c7299d2fb6915 to your computer and use it in GitHub Desktop.
Save kassane/568786d4a177cf85aa7c7299d2fb6915 to your computer and use it in GitHub Desktop.
Dragonbox: float format (based on https://github.com/jk-jeon/dragonbox)
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