Files
mruby-mruby/tools/gen_pow10_tab.rb
Yukihiro "Matz" Matsumoto 9ff1aa9d55 fp_uscale.c: replace fmt_fp.c and readfloat.c with uscale algorithm
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>
2026-04-23 19:25:21 +09:00

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