summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libstd/floatfmt.myr195
1 files changed, 195 insertions, 0 deletions
diff --git a/libstd/floatfmt.myr b/libstd/floatfmt.myr
new file mode 100644
index 0000000..370385d
--- /dev/null
+++ b/libstd/floatfmt.myr
@@ -0,0 +1,195 @@
+use "bigint.use"
+use "types.use"
+use "floatbits.use"
+use "extremum.use"
+use "utf.use"
+use "alloc.use"
+use "slpush.use"
+use "die.use"
+
+pkg std =
+ const float64bfmt : (buf : byte[:], val : float64, mode : int, precision : int -> size)
+ const float32bfmt : (buf : byte[:], val : float32, mode : int, precision : int -> size)
+;;
+
+const Dblbias = 1023
+const Fltbias = 127
+
+const float64bfmt = {buf, val, mode, precision
+ var isneg, exp, mant
+
+ (isneg, mant, exp) = float64explode(val)
+ -> dragon4(buf, isneg, mant, (exp - 52) castto(int64), Dblbias, mode, precision)
+}
+
+const float32bfmt = {buf, val, mode, precision
+ var isneg, exp, mant
+
+ (isneg, mant, exp) = float32explode(val)
+ -> dragon4(buf, isneg, mant castto(int64), (exp - 52) castto(int64), Fltbias, mode, precision)
+}
+
+/*
+buf: output buffer
+e: exponent
+p: precision
+f: mantissa
+
+floating value: x = f^(e - p)
+*/
+const dragon4 = {buf, isneg, f, e, p, mode, cutoff
+ var r, s, t, u, v
+ var udig
+ var mm, mp /* margins above and below */
+ var roundup
+ var low, high
+ var k : int, n : size
+
+ /* if we have zero for the mantissa, we can return early */
+ n = 0
+ if isneg
+ n += encode(buf, '-')
+ ;;
+ if f == 0
+ n += encode(buf[n:], '0')
+ n += encode(buf[n:], '.')
+ n += encode(buf[n:], '0')
+ -> n
+ ;;
+
+ /* initialize */
+ roundup = false
+ r = mkbigint(f)
+ r = bigshli(r, max(e - p, 0))
+ s = bigshli(mkbigint(1), max(0, -(e - p)))
+ mm = bigshli(mkbigint(1), max((e - p), 0))
+ mp = bigdup(mm)
+
+ /* fixup: unequal gaps */
+ t = mkbigint(1)
+ bigshli(t, p - 1)
+ if bigeqi(t, f)
+ bigshli(mp, 1)
+ bigshli(r, 1)
+ bigshli(s, 1)
+ ;;
+ bigfree(t)
+
+ k = 0
+ while true
+ /* r < ceil(s/b) */
+ t = bigdup(s)
+ bigaddi(t, 9)
+ bigdivi(t, 10)
+ match bigcmp(r, t)
+ | `Before:
+ k--
+ bigmuli(r, 10)
+ bigmuli(mm, 10)
+ bigmuli(mp, 10)
+ | _:
+ bigfree(t)
+ break
+ ;;
+ bigfree(t)
+ ;;
+
+ t = bigdup(r)
+ bigshli(t, 1)
+ bigadd(t, mp)
+ while true
+ u = bigdup(s)
+ bigshli(u, 1)
+ match bigcmp(t, u)
+ | `Before:
+ bigfree(u)
+ break
+ | _:
+ k++
+ bigmuli(s, 10)
+ bigfree(u)
+ ;;
+ ;;
+ cutoff = k - buf.len - 1
+ bigfree(t)
+
+ if k <= 0
+ n += encode(buf[n:], '0')
+ n += encode(buf[n:], '.')
+ ;;
+ while true
+ k--
+ bigmuli(r, 10)
+ u = bigdup(r);
+ bigdiv(u, s)
+
+ bigmod(r, s)
+ bigmuli(mm, 10)
+ bigmuli(mp, 10)
+
+ low = false
+ t = bigdup(r)
+ bigshli(t, 1)
+ match bigcmp(t, mm)
+ | `Before: low = true
+ ;;
+ bigfree(t)
+
+ v = bigdup(r)
+ bigshli(v, 1)
+ t = bigdup(s)
+ bigshli(t, 1)
+ bigsub(t, mp)
+ match bigcmp(v, t)
+ | `After: high = true
+ | `Equal: high = roundup
+ | `Before: high = false
+ ;;
+ bigfree(v)
+ bigfree(t)
+ if low || high || k == cutoff
+ break
+ ;;
+ n += format(buf[n:], lowdig(u), k)
+ bigfree(u)
+ ;;
+
+ /* format the last digit */
+ udig = lowdig(u)
+ if low && !high
+ n += format(buf[n:], udig, k)
+ elif high && !low
+ n += format(buf[n:], udig + 1, k)
+ else
+ bigmuli(r, 2)
+ match bigcmp(r, s)
+ | `Before: n += format(buf[n:], udig, k)
+ | `Equal: n += format(buf[n:], udig, k)
+ | `After: n += format(buf[n:], udig + 1, k)
+ ;;
+ ;;
+ -> n
+}
+
+const lowdig = {u
+ if u.dig.len > 0
+ -> u.dig[0]
+ ;;
+ -> 0
+}
+
+const format = {buf, d, k
+ const dig = "0123456789"
+ var n, i
+
+ n = 0
+ if k == 0
+ n += encode(buf[n:], '.')
+ elif k < 0
+ for i = 0; i < -k; i++
+ n += encode(buf[n:], '0')
+ ;;
+ ;;
+ buf[n++] = dig[d]
+ -> n
+}