mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
9ff1aa9d55
Replace separate float formatting (fmt_fp.c) and parsing (readfloat.c) implementations with a unified fp_uscale.c using 128-bit unrounded scaling. Both mrb_format_float() and mrb_read_float() now share a single pow10 table and uscale() primitive for decimal/binary conversion. This fixes subnormal parsing accuracy (old code returned 0.0 for the smallest subnormals) and corrects %.2f rounding for values like 12345.125. Table size grows from ~5KB to ~11KB in .rodata. Co-authored-by: Claude <noreply@anthropic.com>
69 lines
1.6 KiB
Ruby
Executable File
69 lines
1.6 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
# frozen_string_literal: true
|
|
|
|
# Generate pow10 table for mruby unrounded scaling (fp_uscale.c)
|
|
#
|
|
# Uses exact integer arithmetic only.
|
|
# For each p in [-343, 341], computes (hi, lo) such that:
|
|
# 10^p ~= (hi * 2^64 - lo) * 2^pe
|
|
# where pe = floor(p * log2(10)) - 127
|
|
#
|
|
# The 128-bit value pm = hi * 2^64 - lo is in [2^127, 2^128).
|
|
|
|
POW10_MIN = -343
|
|
POW10_MAX = 341
|
|
|
|
def generate_entry(p)
|
|
if p >= 0
|
|
val = 10**p
|
|
bit_len = val.bit_length
|
|
pe = bit_len - 128
|
|
if pe >= 0
|
|
mask = (1 << pe) - 1
|
|
pm = (val >> pe) + ((val & mask) != 0 ? 1 : 0)
|
|
else
|
|
pm = val << (-pe)
|
|
end
|
|
else
|
|
abs_p = -p
|
|
denom = 10**abs_p
|
|
# pe = floor(p * log2(10)) - 127
|
|
# Ruby's integer division of negative numbers does floor division
|
|
pe_est = (p * 108853 >> 15) - 127
|
|
|
|
numerator = 1 << (-pe_est)
|
|
pm = (numerator + denom - 1) / denom
|
|
|
|
# Adjust pe if pm is out of range [2^127, 2^128)
|
|
while pm >= (1 << 128)
|
|
pe_est += 1
|
|
numerator = 1 << (-pe_est)
|
|
pm = (numerator + denom - 1) / denom
|
|
end
|
|
while pm < (1 << 127)
|
|
pe_est -= 1
|
|
numerator = 1 << (-pe_est)
|
|
pm = (numerator + denom - 1) / denom
|
|
end
|
|
end
|
|
|
|
raise "pm out of range for p=#{p}: #{pm.bit_length}" unless pm.bit_length == 128
|
|
|
|
hi = (pm >> 64) + ((pm & ((1 << 64) - 1)) != 0 ? 1 : 0)
|
|
lo = (hi << 64) - pm
|
|
|
|
raise "hi out of range for p=#{p}" unless hi >= (1 << 63) && hi < (1 << 64)
|
|
|
|
{ hi: hi, lo: lo }
|
|
end
|
|
|
|
def main
|
|
entries = (POW10_MIN..POW10_MAX).map { |p| [p, generate_entry(p)] }
|
|
|
|
entries.each do |p, e|
|
|
printf(" {0x%016xULL, 0x%016xULL},\n", e[:hi], e[:lo])
|
|
end
|
|
end
|
|
|
|
main
|