ref: 9afaec2ee7ed8864f9f28c39533e2f2094301fba
dir: /lib/std/fltfmt.myr/
use "alloc"
use "bigint"
use "die"
use "extremum"
use "fltbits"
use "slpush"
use "strbuf"
use "types"
use "utf"
use "memops"
pkg std =
pkglocal const MNormal = 0
pkglocal const MAbsolute = 1
pkglocal const MRelative = 2
pkglocal const flt64bfmt : (sb : strbuf#, val : flt64, mode : int, precision : int -> void)
pkglocal const flt32bfmt : (sb : strbuf#, val : flt32, mode : int, precision : int -> void)
;;
const Dblbias = 1023
const Fltbias = 127
const flt64bfmt = {sb, val, mode, precision
var isneg, exp, mant
(isneg, exp, mant) = flt64explode(val)
exp = max(exp, 1 - Dblbias)
dragon4(sb, isneg, mant, exp - 52, Dblbias, mode, precision)
}
const flt32bfmt = {sb, val, mode, precision
var isneg, exp, mant
(isneg, exp, mant) = flt32explode(val)
exp = (max((exp : int64), 1 - Fltbias) : int32)
dragon4(sb, isneg, (mant : uint64), (exp - 23 : int64), Fltbias, mode, precision)
}
/*
sb: output buffer
e: exponent
p: precision
f: mantissa
floating value: x = f^(e - p)
*/
const dragon4 = {sb, isneg, f, e, p, mode, cutoff
var r, s, t, u, v, y
var udig
var mm, mp /* margins above and below */
var roundup
var low, high
var k
var a, i
if isneg
sbputs(sb, "-")
;;
/* if we have zero for the mantissa, we can return early */
if f == 0
sbputs(sb, "0.0")
-> void
;;
/* initialize */
roundup = false
low = false
high = false
u = mkbigint(0)
r = bigshli(mkbigint(f), max(e, 0))
s = bigshli(mkbigint(1), max(0, -e))
mm = bigshli(mkbigint(1), max(e, 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)
;;
while true
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)
;;
;;
if mode == MNormal
cutoff = k
else
if mode == MRelative
cutoff += k - 1
;;
/* common between relative and absolute */
a = cutoff - k - 1
y = bigdup(s)
if a < 0
for i = 0; i < a; i++
bigmuli(y, 10)
;;
else
for i = 0; i < -a; i++
bigaddi(y, 9)
bigdivi(y, 10)
;;
;;
match bigcmp(y, mm)
| `Before: /* nothing */
| _:
bigfree(mm)
mm = y
;;
match bigcmp(y, mp)
| `Before: /* nothing */
| _:
bigfree(mp)
mp = y
roundup = true
;;
;;
u = bigdup(s)
bigshli(u, 1)
match bigcmp(t, u)
| `Before:
bigfree(t)
bigfree(u)
break
| _:
;;
;;
if k <= 0
sbputs(sb, "0.")
for var i = 0; i < -k; i++
sbputs(sb, "0")
;;
;;
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 = 2*r */
v = bigdup(r)
bigshli(v, 1)
/* t = 2*s - mp */
t = bigdup(s)
bigshli(t, 1)
bigsub(t, mp)
match bigcmp(v, t)
| `Before: high = false;
| `Equal: high = roundup;
| `After: high = true;
;;
bigfree(v)
bigfree(t)
if low || high || k == cutoff
break
;;
format(sb, lowdig(u), k)
bigfree(u)
;;
/* format the last digit */
udig = lowdig(u)
if low && !high
format(sb, udig, k)
elif high && !low
format(sb, udig + 1, k)
else
bigmuli(r, 2)
match bigcmp(r, s)
| `Before: format(sb, udig, k)
| `Equal: format(sb, udig, k)
| `After: format(sb, udig + 1, k)
;;
;;
k--
while k >= -1
format(sb, 0, k--)
;;
bigfree(u)
bigfree(r)
bigfree(s)
bigfree(mm)
bigfree(mp)
}
const lowdig = {u
if u.dig.len > 0
-> u.dig[0]
;;
-> 0
}
const format = {sb, d, k
const dig = "0123456789"
sbputb(sb, dig[d])
if k == 0
sbputs(sb, ".")
;;
}