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:
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;
};