hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit df6d453e299fe7db6555f0b609b37936aaba348d
parent 39c63753f63075ff959019905813a1ed0aea8d86
Author: Joe Finney <me@spxtr.net>
Date:   Sun,  8 Sep 2024 19:28:04 +0100

strconv: merge multiprecision implementations.

Signed-off-by: Joe Finney <me@spxtr.net>

Diffstat:
Mmakefiles/freebsd.aarch64.mk | 2+-
Mmakefiles/freebsd.riscv64.mk | 2+-
Mmakefiles/freebsd.x86_64.mk | 2+-
Mmakefiles/linux.aarch64.mk | 2+-
Mmakefiles/linux.riscv64.mk | 2+-
Mmakefiles/linux.x86_64.mk | 2+-
Mmakefiles/netbsd.aarch64.mk | 2+-
Mmakefiles/netbsd.riscv64.mk | 2+-
Mmakefiles/netbsd.x86_64.mk | 2+-
Mmakefiles/openbsd.aarch64.mk | 2+-
Mmakefiles/openbsd.riscv64.mk | 2+-
Mmakefiles/openbsd.x86_64.mk | 2+-
Astrconv/decimal.ha | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mstrconv/ftos.ha | 164++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Dstrconv/ftos_multiprecision.ha | 305------------------------------------------------------------------------------
Mstrconv/stof.ha | 225+++++++------------------------------------------------------------------------
16 files changed, 359 insertions(+), 561 deletions(-)

diff --git a/makefiles/freebsd.aarch64.mk b/makefiles/freebsd.aarch64.mk @@ -135,7 +135,7 @@ $(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/errors.td $(HAR @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha) -strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha +strconv_ha = strconv/decimal.ha strconv/ftos.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha $(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" diff --git a/makefiles/freebsd.riscv64.mk b/makefiles/freebsd.riscv64.mk @@ -135,7 +135,7 @@ $(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/errors.td $(HAR @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha) -strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha +strconv_ha = strconv/decimal.ha strconv/ftos.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha $(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" diff --git a/makefiles/freebsd.x86_64.mk b/makefiles/freebsd.x86_64.mk @@ -135,7 +135,7 @@ $(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/errors.td $(HAR @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha) -strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha +strconv_ha = strconv/decimal.ha strconv/ftos.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha $(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" diff --git a/makefiles/linux.aarch64.mk b/makefiles/linux.aarch64.mk @@ -153,7 +153,7 @@ $(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/errors.td $(HAR @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha) -strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha +strconv_ha = strconv/decimal.ha strconv/ftos.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha $(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" diff --git a/makefiles/linux.riscv64.mk b/makefiles/linux.riscv64.mk @@ -153,7 +153,7 @@ $(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/errors.td $(HAR @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha) -strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha +strconv_ha = strconv/decimal.ha strconv/ftos.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha $(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" diff --git a/makefiles/linux.x86_64.mk b/makefiles/linux.x86_64.mk @@ -153,7 +153,7 @@ $(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/errors.td $(HAR @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha) -strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha +strconv_ha = strconv/decimal.ha strconv/ftos.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha $(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" diff --git a/makefiles/netbsd.aarch64.mk b/makefiles/netbsd.aarch64.mk @@ -135,7 +135,7 @@ $(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/bytes.td $(HARE @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha) -strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha +strconv_ha = strconv/decimal.ha strconv/ftos.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha $(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" diff --git a/makefiles/netbsd.riscv64.mk b/makefiles/netbsd.riscv64.mk @@ -135,7 +135,7 @@ $(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/bytes.td $(HARE @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha) -strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha +strconv_ha = strconv/decimal.ha strconv/ftos.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha $(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" diff --git a/makefiles/netbsd.x86_64.mk b/makefiles/netbsd.x86_64.mk @@ -135,7 +135,7 @@ $(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/bytes.td $(HARE @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha) -strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha +strconv_ha = strconv/decimal.ha strconv/ftos.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha $(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" diff --git a/makefiles/openbsd.aarch64.mk b/makefiles/openbsd.aarch64.mk @@ -135,7 +135,7 @@ $(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/errors.td $(HAR @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha) -strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha +strconv_ha = strconv/decimal.ha strconv/ftos.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha $(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" diff --git a/makefiles/openbsd.riscv64.mk b/makefiles/openbsd.riscv64.mk @@ -135,7 +135,7 @@ $(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/errors.td $(HAR @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha) -strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha +strconv_ha = strconv/decimal.ha strconv/ftos.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha $(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" diff --git a/makefiles/openbsd.x86_64.mk b/makefiles/openbsd.x86_64.mk @@ -135,7 +135,7 @@ $(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/errors.td $(HAR @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha) -strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha +strconv_ha = strconv/decimal.ha strconv/ftos.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha $(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" diff --git a/strconv/decimal.ha b/strconv/decimal.ha @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> +// (c) 2010 The Go Authors. All rights reserved. + +def maxshift: u8 = 60; +def decimal_point_range: u16 = 2047; + +type decimal = struct { + // Numbers 0-9, not ascii, big endian. Length for small numbers is + // log10(mantissa * 5^-exp). Subnormal doubles have min exp -1074 and + // max mantissa 4e16, giving at most 767 digits. + digits: [800]u8, + + // Number of valid digits. May be 0 if the number rounds to 0. + nd: size, + + // Decimal point index, may be negative. + // -1 means 0.0ddd..., 0 means 0.ddd..., 1 means d.dd..., and so on. + dp: i32, + + negative: bool, + + // Were there nonzero digits beyond digits[0..nd]? This affects + // rounding. + truncated: bool, +}; + +// remove trailing zeroes +fn trim(d: *decimal) void = { + for (d.nd > 0 && d.digits[d.nd - 1] == 0) { + d.nd -= 1; + }; +}; + +fn leftshift_newdigits(d: *decimal, shift: u32) u32 = { + shift &= 63; + let x_a = left_shift_table[shift]: u32; + let x_b = left_shift_table[shift + 1]: u32; + let nn = x_a >> 11; + let pow5_a = 0x7FF & x_a, pow5_b = 0x7FF & x_b; + const p5 = pow5_table[pow5_a..]; + let i = 0u32, n = pow5_b - pow5_a; + for (i < n; i += 1) { + if (i >= d.nd) { + return nn - 1; + } else if (d.digits[i] == p5[i]) { + continue; + } else if (d.digits[i] < p5[i]) { + return nn - 1; + } else { + return nn; + }; + }; + return nn; +}; + +fn leftshift(d: *decimal, k: u32) void = { + assert(k <= maxshift); + if (d.nd == 0) return; + let nn = leftshift_newdigits(d, k); + let r = d.nd: int - 1, w = r: size + nn; + let n = 0u64; + for (r >= 0) { + n += d.digits[r]: u64 << k; + const quo = n / 10, rem = n - 10 * quo; + if (w < len(d.digits)) { + d.digits[w] = rem: u8; + } else if (rem != 0) { + d.truncated = true; + }; + n = quo; + r -= 1; + w -= 1; + }; + for (n > 0) { + const quo = n / 10, rem = n - 10 * quo; + if (w < len(d.digits)) { + d.digits[w] = rem: u8; + } else if (rem != 0) { + d.truncated = true; + }; + n = quo; + w -= 1; + }; + d.nd += nn; + if (d.nd > len(d.digits)) { + d.nd = len(d.digits); + }; + d.dp += nn: i32; + trim(d); +}; + +fn rightshift(d: *decimal, k: u32) void = { + let r = 0z, w = 0z, n = 0u64; + for (n >> k == 0; r += 1) { + if (r >= d.nd) { + if (n == 0) { + d.nd = 0; + return; + }; + for (n >> k == 0; r += 1) { + n *= 10; + }; + break; + }; + n = n * 10 + d.digits[r]; + }; + d.dp -= r: i32 - 1; + if (d.dp < -(decimal_point_range: i32)) { + *d = decimal { ... }; + return; + }; + const mask = (1u64 << k) - 1; + for (r < d.nd; r += 1) { + const dig = n >> k; + n &= mask; + d.digits[w] = dig: u8; + w += 1; + n = n * 10 + d.digits[r]; + }; + for (n > 0) { + const dig = n >> k; + n &= mask; + if (w < len(d.digits)) { + d.digits[w] = dig: u8; + w += 1; + } else if (dig > 0) { + d.truncated = true; + }; + n *= 10; + }; + d.nd = w; + trim(d); +}; + +// Shift right (k < 0) or left (k > 0). We can only shift up to 60 at a time +// without losing bits, so break up big shifts. +fn decimal_shift(d: *decimal, k: int) void = { + if (d.nd == 0) return; + if (k > 0) { + for (k > maxshift: int) { + leftshift(d, maxshift); + k -= maxshift: i32; + }; + leftshift(d, k: u32); + } else if (k < 0) { + for (k < -(maxshift: int)) { + rightshift(d, maxshift); + k += maxshift: i32; + }; + rightshift(d, (-k): u32); + }; +}; + +fn should_round_up(d: *decimal, nd: uint) bool = if (nd < d.nd) { + if (d.digits[nd] == 5 && nd + 1 == d.nd) { + return d.truncated || + (nd > 0 && d.digits[nd - 1] & 1 != 0); + } else return d.digits[nd] >= 5; +} else false; + +fn round(d: *decimal, nd: uint) void = { + if (nd >= d.nd) return; + if (should_round_up(d, nd)) roundup(d, nd) + else rounddown(d, nd); +}; + +fn rounddown(d: *decimal, nd: uint) void = { + if (nd >= d.nd) return; + d.nd = nd; + trim(d); +}; + +fn roundup(d: *decimal, nd: uint) void = { + if (nd >= d.nd) return; + for (let i = nd: int - 1; i >= 0; i -= 1) { + if (d.digits[i] < 9) { + d.digits[i] += 1; + d.nd = i: size + 1; + return; + }; + }; + d.digits[0] = 1; + d.nd = 1; + d.dp += 1; +}; + +fn decimal_round(d: *decimal) u64 = { + if (d.nd == 0 || d.dp < 0) return 0; + if (d.dp > 18) return ~0u64; + let i = 0z, n: u64 = 0; + for (i < d.dp: uint && i < d.nd; i += 1) { + n = n * 10 + d.digits[i]; + }; + for (i < d.dp: uint; i += 1) { + n *= 10; + }; + if (should_round_up(d, d.dp: uint)) { + n += 1; + }; + return n; +}; diff --git a/strconv/ftos.ha b/strconv/ftos.ha @@ -106,8 +106,8 @@ fn encode_zero( return z; }; -fn encode_f_mp( - m: *mp, +fn encode_f_dec( + dec: *decimal, h: io::handle, f: ffmt, prec: (void | uint), @@ -116,40 +116,40 @@ fn encode_f_mp( // we will loop from lo <= i < hi, printing either zeros or a digit. // lo is simple, but hi depends intricately on f, prec, and the // SHOW_POINT flag. - const lo = if (m.dp <= 0) m.dp - 1 else 0; + const lo = if (dec.dp <= 0) dec.dp - 1 else 0i32; let hi = match (prec) { case void => - yield if (m.nd: int > m.dp) m.nd: int else m.dp; + yield if (dec.nd: i32 > dec.dp) dec.nd: i32 else dec.dp; case let u: uint => - yield if (m.dp <= 0) lo + u: int + 1 else m.dp + u: int; + yield if (dec.dp <= 0) lo + u: i32 + 1 else dec.dp + u: i32; }; // ffmt::G: we need to remove trailing zeros if (f == ffmt::G) { // first, make sure we include at least prec digits if (prec is uint) { const p = prec as uint; - if (m.dp <= 0 && hi < p: int) { + if (dec.dp <= 0 && hi < p: i32) { hi = p: int; }; }; // then, cut back to the decimal point or nd - if (hi > m.nd: int && m.dp <= 0) { - hi = m.nd: int; - } else if (hi > m.dp && m.dp > 0) { - hi = if (m.nd: int > m.dp) m.nd: int else m.dp; + if (hi > dec.nd: i32 && dec.dp <= 0) { + hi = dec.nd: int; + } else if (hi > dec.dp && dec.dp > 0) { + hi = if (dec.nd: i32 > dec.dp) dec.nd: int else dec.dp; }; }; // SHOW_POINT: we need to go at least one past the decimal - if (ffpoint(flag) && hi <= m.dp) { - hi = m.dp + 1; + if (ffpoint(flag) && hi <= dec.dp) { + hi = dec.dp + 1; }; let z = 0z; for (let i = lo; i < hi; i += 1) { - if (i == m.dp) { + if (i == dec.dp) { z += memio::appendrune(h, '.')?; }; - if (0 <= i && i < m.nd: int) { - z += memio::appendrune(h, (m.buf[i] + '0'): rune)?; + if (0 <= i && i < dec.nd: i32) { + z += memio::appendrune(h, (dec.digits[i] + '0'): rune)?; } else { z += memio::appendrune(h, '0')?; }; @@ -157,42 +157,42 @@ fn encode_f_mp( return z; }; -fn encode_e_mp( - m: *mp, +fn encode_e_dec( + dec: *decimal, h: io::handle, f: ffmt, prec: (void | uint), flag: fflags, ) (size | io::error) = { let z = 0z; - assert(m.nd > 0); - z += memio::appendrune(h, (m.buf[0] + '0'): rune)?; + assert(dec.nd > 0); + z += memio::appendrune(h, (dec.digits[0] + '0'): rune)?; const zeros: uint = match (prec) { case void => yield 0; case let u: uint => yield switch (f) { case ffmt::G => - yield if (m.nd + 1 < u) u - m.nd + 1 else 0; + yield if (dec.nd + 1 < u) u - dec.nd: uint + 1 else 0; case ffmt::E => - yield if (m.nd < u + 1) u - m.nd + 1 else 0; + yield if (dec.nd < u + 1) u - dec.nd: uint + 1 else 0; case => abort(); }; }; - if (m.nd <= 1 && ffpoint(flag) && zeros < 1) { + if (dec.nd <= 1 && ffpoint(flag) && zeros < 1) { zeros = 1; }; - if (m.nd > 1 || zeros > 0) { + if (dec.nd > 1 || zeros > 0) { z += memio::appendrune(h, '.')?; }; - for (let i = 1z; i < m.nd; i += 1) { - z += memio::appendrune(h, (m.buf[i] + '0'): rune)?; + for (let i = 1z; i < dec.nd; i += 1) { + z += memio::appendrune(h, (dec.digits[i] + '0'): rune)?; }; for (let i = 0u; i < zeros; i += 1) { z += memio::appendrune(h, '0')?; }; z += memio::appendrune(h, if (ffcaps_exp(flag)) 'E' else 'e')?; - let e = m.dp - 1; + let e = dec.dp - 1; if (e < 0) { e = -e; z += memio::appendrune(h, '-')?; @@ -214,6 +214,90 @@ fn encode_e_mp( return z; }; +fn init_dec_mant_exp(d: *decimal, mantissa: u64, exponent: i32) void = { + const dl = declen(mantissa); + for (let i = 0u; i < dl; i += 1) { + d.digits[dl - i - 1] = (mantissa % 10): u8; + mantissa /= 10; + }; + d.nd = dl; + d.dp = dl: i32 + exponent; +}; + +fn init_dec( + dec: *decimal, + mantissa: u64, + exponent: u32, + eb: u64, + mb: u64, +) void = { + let e2 = (eb + mb): i32; + let m2: u64 = 0; + if (exponent == 0) { + e2 = 1 - e2; + m2 = mantissa; + } else { + e2 = (exponent: i32) - e2; + m2 = (1u64 << mb) | mantissa; + }; + + dec.nd = declen(m2); + dec.dp = dec.nd: int; + for (let i = 0z; i < dec.nd; i += 1) { + dec.digits[dec.nd - i - 1] = (m2 % 10): u8; + m2 /= 10; + }; + decimal_shift(dec, e2); +}; + +// Compute the number of figs to round to for the given arguments. +fn compute_round( + dec: *decimal, + f: ffmt, + prec: (void | uint), + flag: fflags, +) uint = { + // nd is the number of sig figs that we want to end up with + let nd: int = match (prec) { + case void => + // we should only get here if Ryu did not extend past the + // decimal point + assert(ffpoint(flag)); + yield dec.nd: int + (if (dec.dp > 0) dec.dp: int else 0); + case let u: uint => + yield switch (f) { + case ffmt::E => + yield u: int + 1; + case ffmt::F => + yield u: int + dec.dp: int; + case ffmt::G => + yield if (u == 0) 1 else u: int; + }; + }; + const nde = if (nd < 2) 2 else nd; + const ndf = if (dec.dp >= 0 && nd: int < dec.dp: int + 1) dec.dp + 1 + else nd; + if (ffpoint(flag)) { + nd = switch (f) { + case ffmt::E => + // need at least two digits, d.de0. + yield nde; + case ffmt::F => + // need enough to clear the decimal point by one. + yield ndf: int; + case ffmt::G => + // XXX: dup'd with the condition in ftosf_handle + if (dec.dp < -1 || dec.dp: int - dec.nd: int > 2) + yield nde: int; + yield ndf: int; + }; + }; + if (nd <= 0) { + nd = 0; + }; + return if (nd: uint > dec.nd) dec.nd: uint else nd: uint; +}; + // Converts a [[types::floating]] to a string in base 10 and writes the result // to the provided handle. Format parameters are as in [[ftosf]]. export fn fftosf( @@ -262,7 +346,7 @@ export fn fftosf( return z + encode_zero(h, f, prec, flag)?; }; - let m = mp { ... }; + let dec = decimal { ... }; let ok = false; if (prec is void) { // Shortest via Ryƫ. It is not correct to use f64todecf64 for @@ -275,43 +359,45 @@ export fn fftosf( const d = f32todecf32(mantissa: u32, exponent); yield (d.mantissa: u64, d.exponent); }; - init_mp_dec(&m, mdec, edec); + init_dec_mant_exp(&dec, mdec, edec); // If SHOW_POINT and we have too few digits, then we need to // fall back to multiprecision. - ok = !ffpoint(flag) || m.dp < m.nd: int; + ok = !ffpoint(flag) || dec.dp < dec.nd: i32; }; if (!ok) { // Fall back to multiprecision. match (n) { case f64 => - init_mp(&m, mantissa, exponent, math::F64_EXPONENT_BIAS, + init_dec(&dec, mantissa, exponent, + math::F64_EXPONENT_BIAS, math::F64_MANTISSA_BITS); case f32 => - init_mp(&m, mantissa, exponent, math::F32_EXPONENT_BIAS, + init_dec(&dec, mantissa, exponent, + math::F32_EXPONENT_BIAS, math::F32_MANTISSA_BITS); }; - trim_mp(&m); - const nd = compute_round_mp(&m, f, prec, flag); - round_mp(&m, nd); + trim(&dec); + const nd = compute_round(&dec, f, prec, flag); + round(&dec, nd); }; if (f == ffmt::G) { - trim_mp(&m); + trim(&dec); }; if (f == ffmt::G && prec is uint) { if (prec as uint == 0) prec = 1; }; - if (m.nd == 0) { + if (dec.nd == 0) { // rounded to zero return z + encode_zero(h, f, prec, flag)?; } else if (f == ffmt::E || (f == ffmt::G && - (m.dp < -1 || m.dp - m.nd: int > 2))) { - return z + encode_e_mp(&m, h, f, prec, flag)?; + (dec.dp < -1 || dec.dp - dec.nd: i32 > 2))) { + return z + encode_e_dec(&dec, h, f, prec, flag)?; } else { - return z + encode_f_mp(&m, h, f, prec, flag)?; + return z + encode_f_dec(&dec, h, f, prec, flag)?; }; }; diff --git a/strconv/ftos_multiprecision.ha b/strconv/ftos_multiprecision.ha @@ -1,305 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// (c) Hare authors <https://harelang.org> - -// Multiprecision float to string based on golang's strconv/decimal.go. - -use strings; - -type mp = struct { - // Numbers 0-9, not ascii. Length for small numbers is - // log10(mantissa * 5^-exp). Subnormal doubles have min exp -1074 and - // max mantissa 4e16, giving at most 767 digits. - buf: [768]u8, - // Number of valid digits in buf. May be 0 if the number rounds to 0. - nd: uint, - // Decimal point index, may be negative. - // -1 means 0.0ddd... - // 0 means 0.ddd... - // 1 means d.dd... - // and so on - dp: int, -}; - -// These come from golang. The index into the table is amount of shift, up to -// 60. The number is the count of new digits that will be added by the shift, -// but one fewer if the number's prefix is smaller than the string prefix. -// -// For example, leftcheats[2] is (1, "25"). Any number left shifted by 2 will -// therefore be 1 digit longer, or zero digits longer if its first two digits -// are smaller than 25. -const leftcheats: [](size, str) = [ - (0, ""), - (1, "5"), - (1, "25"), - (1, "125"), - (2, "625"), - (2, "3125"), - (2, "15625"), - (3, "78125"), - (3, "390625"), - (3, "1953125"), - (4, "9765625"), - (4, "48828125"), - (4, "244140625"), - (4, "1220703125"), - (5, "6103515625"), - (5, "30517578125"), - (5, "152587890625"), - (6, "762939453125"), - (6, "3814697265625"), - (6, "19073486328125"), - (7, "95367431640625"), - (7, "476837158203125"), - (7, "2384185791015625"), - (7, "11920928955078125"), - (8, "59604644775390625"), - (8, "298023223876953125"), - (8, "1490116119384765625"), - (9, "7450580596923828125"), - (9, "37252902984619140625"), - (9, "186264514923095703125"), - (10, "931322574615478515625"), - (10, "4656612873077392578125"), - (10, "23283064365386962890625"), - (10, "116415321826934814453125"), - (11, "582076609134674072265625"), - (11, "2910383045673370361328125"), - (11, "14551915228366851806640625"), - (12, "72759576141834259033203125"), - (12, "363797880709171295166015625"), - (12, "1818989403545856475830078125"), - (13, "9094947017729282379150390625"), - (13, "45474735088646411895751953125"), - (13, "227373675443232059478759765625"), - (13, "1136868377216160297393798828125"), - (14, "5684341886080801486968994140625"), - (14, "28421709430404007434844970703125"), - (14, "142108547152020037174224853515625"), - (15, "710542735760100185871124267578125"), - (15, "3552713678800500929355621337890625"), - (15, "17763568394002504646778106689453125"), - (16, "88817841970012523233890533447265625"), - (16, "444089209850062616169452667236328125"), - (16, "2220446049250313080847263336181640625"), - (16, "11102230246251565404236316680908203125"), - (17, "55511151231257827021181583404541015625"), - (17, "277555756156289135105907917022705078125"), - (17, "1387778780781445675529539585113525390625"), - (18, "6938893903907228377647697925567626953125"), - (18, "34694469519536141888238489627838134765625"), - (18, "173472347597680709441192448139190673828125"), - (19, "867361737988403547205962240695953369140625"), -]; - -fn prefix_less_than_mp(m: *mp, s: str) bool = { - const u = strings::toutf8(s); - for (let i = 0z; i < len(s); i += 1) { - if (i >= m.nd) { - return true; - }; - if (m.buf[i] + '0': u8 != u[i]) { - return m.buf[i] + '0': u8 < u[i]; - }; - }; - return false; -}; - -// Shift left by k. -fn shl_mp(m: *mp, k: u64) void = { - let delta = leftcheats[k].0; - if (prefix_less_than_mp(m, leftcheats[k].1)) - delta -= 1; - let r = (m.nd - 1): int; - let w = m.nd + delta; - let n = 0u64; - for (r >= 0; r -= 1) { - n += m.buf[r]: u64 << k; - const quo = n / 10; - const rem = n - 10 * quo; - w -= 1; - m.buf[w] = rem: u8; - n = quo; - }; - for (n > 0) { - const quo = n / 10; - const rem = n - 10 * quo; - w -= 1; - m.buf[w] = rem: u8; - n = quo; - }; - m.nd += delta: uint; - m.dp += delta: int; -}; - -// Shift right by k. -fn shr_mp(m: *mp, k: u64) void = { - let r = 0z; - let w = 0z; - let n = 0u64; - const mask = (1 << k) - 1; - - for (n >> k == 0; r += 1) { - if (r >= m.nd) { - for (n >> k == 0) { - n *= 10; - r += 1; - }; - break; - }; - n = 10 * n + m.buf[r]; - }; - m.dp -= r: int - 1; - - for (r < m.nd; r += 1) { - const c = m.buf[r]; - const dig = n >> k; - n &= mask; - m.buf[w] = dig: u8; - w += 1; - n = n * 10 + c; - }; - - for (n > 0; w += 1) { - const dig = n >> k; - n &= mask; - m.buf[w] = dig: u8; - n = n * 10; - }; - m.nd = w: uint; -}; - -// Shift right (k < 0) or left (k > 0). We can only shift up to 60 at a time -// without losing bits, so break up big shifts. -fn shift_mp(m: *mp, k: int) void = { - if (k < 0) { - let nk = (-k): uint; - for (nk > 60) { - shr_mp(m, 60); - nk -= 60; - }; - shr_mp(m, nk); - } else if (k > 0) { - for (k > 60) { - shl_mp(m, 60); - k -= 60; - }; - shl_mp(m, k: uint); - }; -}; - -fn init_mp(m: *mp, mantissa: u64, exponent: u32, eb: u64, mb: u64) void = { - let e2 = (eb + mb): i32; - let m2: u64 = 0; - if (exponent == 0) { - e2 = 1 - e2; - m2 = mantissa; - } else { - e2 = (exponent: i32) - e2; - m2 = (1u64 << mb) | mantissa; - }; - - m.nd = declen(m2); - m.dp = m.nd: int; - for (let i = 0z; i < m.nd; i += 1) { - m.buf[m.nd - i - 1] = (m2 % 10): u8; - m2 /= 10; - }; - shift_mp(m, e2); -}; - -fn init_mp_dec(m: *mp, mantissa: u64, exponent: i32) void = { - const dl = declen(mantissa); - for (let i = 0u; i < dl; i += 1) { - m.buf[dl - i - 1] = (mantissa % 10): u8; - mantissa /= 10; - }; - m.nd = dl; - m.dp = dl: i32 + exponent; -}; - -fn round_up_mp(m: *mp) void = { - for (let i = 1z; i <= m.nd; i += 1) { - if (m.buf[m.nd - i] < 9) { - m.buf[m.nd - i] += 1; - return; - } else { - m.buf[m.nd - i] = 0; - }; - }; - // All high - m.buf[0] = 1; - m.nd = 1; - m.dp += 1; -}; - -// Compute the number of figs to round to for the given arguments. -fn compute_round_mp(m: *mp, f: ffmt, prec: (void | uint), flag: fflags) uint = { - // nd is the number of sig figs that we want to end up with - let nd: int = match (prec) { - case void => - // we should only get here if Ryu did not extend past the - // decimal point - assert(ffpoint(flag)); - yield m.nd: int + (if (m.dp > 0) m.dp else 0); - case let u: uint => - yield switch (f) { - case ffmt::E => - yield u: int + 1; - case ffmt::F => - yield u: int + m.dp; - case ffmt::G => - yield if (u == 0) 1 else u: int; - }; - }; - const nde = if (nd < 2) 2 else nd; - const ndf = if (m.dp >= 0 && nd < m.dp + 1) m.dp + 1 else nd; - if (ffpoint(flag)) { - nd = switch (f) { - case ffmt::E => - // need at least two digits, d.de0. - yield nde; - case ffmt::F => - // need enough to clear the decimal point by one. - yield ndf; - case ffmt::G => - // XXX: dup'd with the condition in ftosf_handle - if (m.dp < -1 || m.dp - m.nd: int > 2) - yield nde; - yield ndf; - }; - }; - if (nd <= 0) { - nd = 0; - }; - return if (nd: uint > m.nd) m.nd else nd: uint; -}; - -fn round_mp(m: *mp, nd: uint) void = { - assert(nd <= m.nd); - if (nd == m.nd) - return; - const oldnd = m.nd; - m.nd = nd; - if (m.buf[nd] > 5) { - round_up_mp(m); - } else if (m.buf[nd] == 5) { - let gt = false; - for (let i = m.nd + 1; i < oldnd; i += 1) { - if (m.buf[i] > 0) { - round_up_mp(m); - gt = true; - break; - }; - }; - if (!gt && nd > 0 && m.buf[nd - 1] & 1 > 0) { - round_up_mp(m); - }; - }; -}; - -// Remove trailing zeros. -fn trim_mp(m: *mp) void = { - for (m.nd > 1 && m.buf[m.nd - 1] == 0) { - m.nd -= 1; - }; -}; diff --git a/strconv/stof.ha b/strconv/stof.ha @@ -11,191 +11,6 @@ use ascii; use math; use strings; -def maxshift: u8 = 60; -def decimal_point_range: u16 = 2047; - -type decimal = struct { - num_digits: size, - decimal_point: i32, - negative: bool, - truncated: bool, - digits: [800]u8, -}; - -// remove trailing zeroes -fn trim(d: *decimal) void = { - for (d.num_digits > 0 && d.digits[d.num_digits - 1] == 0) { - d.num_digits -= 1; - }; -}; - -fn leftshift_newdigits(d: *decimal, shift: u32) u32 = { - shift &= 63; - let x_a = left_shift_table[shift]: u32; - let x_b = left_shift_table[shift + 1]: u32; - let nn = x_a >> 11; - let pow5_a = 0x7FF & x_a, pow5_b = 0x7FF & x_b; - const p5 = pow5_table[pow5_a..]; - let i = 0u32, n = pow5_b - pow5_a; - for (i < n; i += 1) { - if (i >= d.num_digits) { - return nn - 1; - } else if (d.digits[i] == p5[i]) { - continue; - } else if (d.digits[i] < p5[i]) { - return nn - 1; - } else { - return nn; - }; - }; - return nn; -}; - -fn leftshift(d: *decimal, k: u32) void = { - assert(k <= maxshift); - if (d.num_digits == 0) return; - let nn = leftshift_newdigits(d, k); - let r = d.num_digits: int - 1, w = r: size + nn; - let n = 0u64; - for (r >= 0) { - n += d.digits[r]: u64 << k; - const quo = n / 10, rem = n - 10 * quo; - if (w < len(d.digits)) { - d.digits[w] = rem: u8; - } else if (rem != 0) { - d.truncated = true; - }; - n = quo; - r -= 1; - w -= 1; - }; - for (n > 0) { - const quo = n / 10, rem = n - 10 * quo; - if (w < len(d.digits)) { - d.digits[w] = rem: u8; - } else if (rem != 0) { - d.truncated = true; - }; - n = quo; - w -= 1; - }; - d.num_digits += nn; - if (d.num_digits > len(d.digits)) { - d.num_digits = len(d.digits); - }; - d.decimal_point += nn: i32; - trim(d); -}; - -fn rightshift(d: *decimal, k: u32) void = { - let r = 0z, w = 0z, n = 0u64; - for (n >> k == 0; r += 1) { - if (r >= d.num_digits) { - if (n == 0) { - d.num_digits = 0; - return; - }; - for (n >> k == 0; r += 1) { - n *= 10; - }; - break; - }; - n = n * 10 + d.digits[r]; - }; - d.decimal_point -= r: i32 - 1; - if (d.decimal_point < -(decimal_point_range: i32)) { - *d = decimal { ... }; - return; - }; - const mask = (1u64 << k) - 1; - for (r < d.num_digits; r += 1) { - const dig = n >> k; - n &= mask; - d.digits[w] = dig: u8; - w += 1; - n = n * 10 + d.digits[r]; - }; - for (n > 0) { - const dig = n >> k; - n &= mask; - if (w < len(d.digits)) { - d.digits[w] = dig: u8; - w += 1; - } else if (dig > 0) { - d.truncated = true; - }; - n *= 10; - }; - d.num_digits = w; - trim(d); -}; - -fn decimal_shift(d: *decimal, k: int) void = { - if (d.num_digits == 0) return; - if (k > 0) { - for (k > maxshift: int) { - leftshift(d, maxshift); - k -= maxshift: i32; - }; - leftshift(d, k: u32); - } else if (k < 0) { - for (k < -(maxshift: int)) { - rightshift(d, maxshift); - k += maxshift: i32; - }; - rightshift(d, (-k): u32); - }; -}; - -fn should_round_up(d: *decimal, nd: uint) bool = if (nd < d.num_digits) { - if (d.digits[nd] == 5 && nd + 1 == d.num_digits) { - return d.truncated || - (nd > 0 && d.digits[nd - 1] & 1 != 0); - } else return d.digits[nd] >= 5; -} else false; - -fn round(d: *decimal, nd: uint) void = { - if (nd >= d.num_digits) return; - if (should_round_up(d, nd)) roundup(d, nd) - else rounddown(d, nd); -}; - -fn rounddown(d: *decimal, nd: uint) void = { - if (nd >= d.num_digits) return; - d.num_digits = nd; - trim(d); -}; - -fn roundup(d: *decimal, nd: uint) void = { - if (nd >= d.num_digits) return; - for (let i = nd: int - 1; i >= 0; i -= 1) { - if (d.digits[i] < 9) { - d.digits[i] += 1; - d.num_digits = i: size + 1; - return; - }; - }; - d.digits[0] = 1; - d.num_digits = 1; - d.decimal_point += 1; -}; - -fn decimal_round(d: *decimal) u64 = { - if (d.num_digits == 0 || d.decimal_point < 0) return 0; - if (d.decimal_point > 18) return ~0u64; - let i = 0z, n: u64 = 0; - for (i < d.decimal_point: uint && i < d.num_digits; i += 1) { - n = n * 10 + d.digits[i]; - }; - for (i < d.decimal_point: uint; i += 1) { - n *= 10; - }; - if (should_round_up(d, d.decimal_point: uint)) { - n += 1; - }; - return n; -}; - fn todig(c: u8) u8 = { if ('0' <= c && c <= '9') { return c - '0'; @@ -313,16 +128,16 @@ fn decimal_parse(d: *decimal, s: str) (void | invalid) = { if (buf[i] == '.') { if (sawdot) return i: invalid; sawdot = true; - d.decimal_point = d.num_digits: int; + d.dp = d.nd: int; } else if (ascii::isdigit(buf[i]: rune)) { sawdigits = true; - if (buf[i] == '0' && d.num_digits == 0) { - d.decimal_point -= 1; + if (buf[i] == '0' && d.nd == 0) { + d.dp -= 1; continue; }; - if (d.num_digits < len(d.digits)) { - d.digits[d.num_digits] = buf[i] - '0'; - d.num_digits += 1; + if (d.nd < len(d.digits)) { + d.digits[d.nd] = buf[i] - '0'; + d.nd += 1; } else if (buf[i] != '0') { d.truncated = true; }; @@ -330,7 +145,7 @@ fn decimal_parse(d: *decimal, s: str) (void | invalid) = { }; if (!sawdigits) return i: invalid; if (!sawdot) { - d.decimal_point = d.num_digits: int; + d.dp = d.nd: int; }; if (i < len(s) && (buf[i] == 'e' || buf[i] == 'E')) { i += 1; @@ -350,7 +165,7 @@ fn decimal_parse(d: *decimal, s: str) (void | invalid) = { e = e * 10 + (buf[i] - '0'): int; }; }; - d.decimal_point += e * expsign; + d.dp += e * expsign; }; if (i != len(s)) return i: invalid; }; @@ -435,37 +250,37 @@ fn floatbits(d: *decimal, f: *math::floatinfo) (u64 | overflow) = { 0, 3, 6, 9, 13, 16, 19, 23, 26, 29, 33, 36, 39, 43, 46, 49, 53, 56, 59, ]; - if (d.num_digits == 0 || d.decimal_point < -326) { + if (d.nd == 0 || d.dp < -326) { return if (d.negative) mkfloat(0, 0, d.negative, f) else 0; - } else if (d.decimal_point > 310) { + } else if (d.dp > 310) { return overflow; }; - if (d.num_digits <= 19) { + if (d.nd <= 19) { let mant = 0u64; - for (let i = 0z; i < d.num_digits; i += 1) { + for (let i = 0z; i < d.nd; i += 1) { mant = (10 * mant) + d.digits[i]; }; - const exp10 = d.decimal_point - d.num_digits: i32; + const exp10 = d.dp - d.nd: i32; const r = eisel_lemire(mant, exp10, d.negative, f); if (r is u64) { return r: u64; }; }; - for (d.decimal_point > 0) { - const n: int = if (d.decimal_point: uint >= len(powtab)) + for (d.dp > 0) { + const n: int = if (d.dp: uint >= len(powtab)) maxshift: int - else powtab[d.decimal_point]; + else powtab[d.dp]; decimal_shift(d, -n); e += n; }; - for (d.decimal_point <= 0) { - const n: int = if (d.decimal_point == 0) { + for (d.dp <= 0) { + const n: int = if (d.dp == 0) { if (d.digits[0] >= 5) break; yield if (d.digits[0] < 2) 2 else 1; - } else if (-d.decimal_point >= len(powtab): i32) + } else if (-d.dp >= len(powtab): i32) maxshift: int - else powtab[-d.decimal_point]; + else powtab[-d.dp]; decimal_shift(d, n); e -= n; };