ref: 1f645d217c2faf9cd08b5f4910ca10479821b434
dir: /lib/std/fltfmt.myr/
use "alloc" use "bigint" use "die" use "extremum" use "fltbits" use "intfmt" use "option" 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 type fltparams = struct mode : int cutoff : size scientific : bool padto : size padfill : char ;; pkglocal const flt64bfmt : (sb : strbuf#, opts : fltparams, val : flt64 -> void) pkglocal const flt32bfmt : (sb : strbuf#, opts : fltparams, val : flt32 -> void) ;; const Dblbias = 1023 const Fltbias = 127 const flt64bfmt = {sb, opts, val var isneg, exp, mant if isnan(val) -> nanfmt(sb, opts) ;; (isneg, exp, mant) = flt64explode(val) if exp > Dblbias -> inffmt(sb, opts, isneg) ;; var valsb = mksb() exp = max(exp, 1 - Dblbias) dragon4(valsb, false, mant, exp - 52, Dblbias, opts.mode, -opts.cutoff, opts.scientific) -> blobfmt(sb, sbfin(valsb), opts, isneg) } const flt32bfmt = {sb, opts, val var isneg, exp, mant if isnan(val) -> nanfmt(sb, opts) ;; (isneg, exp, mant) = flt32explode(val) if (exp : int64) > Fltbias -> inffmt(sb, opts, isneg) ;; var valsb = mksb() exp = (max((exp : int64), 1 - Fltbias) : int32) dragon4(valsb, false, (mant : uint64), (exp - 23 : int64), Fltbias, opts.mode, -opts.cutoff, opts.scientific) -> blobfmt(sb, sbfin(valsb), opts, isneg) } const nanfmt = {sb, opts var npad = clamp(opts.padto - 3, 0, opts.padto) for var j = 0; j < npad; j++ sbputc(sb, opts.padfill) ;; sbputs(sb, "NaN") } const inffmt = {sb, opts, isneg var s = "Inf" var l = 3 if isneg s = "-Inf" l = 4 ;; var npad = clamp(opts.padto - l, 0, opts.padto) for var j = 0; j < npad; j++ sbputc(sb, opts.padfill) ;; sbputs(sb, s) } const blobfmt = {sb, blob, opts, isneg /* No exotic characters, so no need for strcellwidth */ var width = blob.len if isneg width++ ;; var npad = clamp(opts.padto - width, 0, opts.padto) if opts.padfill == '0' && isneg sbputb(sb, ('-' : byte)) ;; for var j = 0; j < npad; j++ sbputc(sb, opts.padfill) ;; if opts.padfill != '0' && isneg sbputb(sb, ('-' : byte)) ;; sbputs(sb, blob) } /* sb: output buffer e: exponent p: precision f: mantissa floating value: x = f^(e - p) */ const dragon4 = {sb, isneg, f, e, p, mode, cutoff, scientific var r, s, t, u, v, y var udig var mm, mp /* margins above and below */ var roundup var low, high var k var tenpower : int64 = 0 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") if scientific sbputs(sb, "e0") ;; -> 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 if cutoff >= 0 cutoff = -1 ;; cutoff += k ;; a = cutoff - k /* common between relative and absolute */ 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 = bigdup(y) ;; match bigcmp(y, mp) | `Before: /* nothing */ | _: bigfree(mp) mp = bigdup(y) roundup = true /* recalculate 2*R + M+ for the loop termination */ bigfree(t) t = bigdup(r) bigshli(t, 1) bigadd(t, mp) ;; bigfree(y) ;; u = bigdup(s) bigshli(u, 1) match bigcmp(t, u) | `Before: bigfree(t) bigfree(u) break | _: ;; ;; if scientific tenpower = (k - 1 : int64) cutoff += (1 - k) k = 1 ;; 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-- if !scientific && cutoff > -1 cutoff = -1 ;; while k >= cutoff format(sb, 0, k--) ;; if scientific sbputs(sb, "e") intfmt(sb, [ .base = 10 ], true, (tenpower : uint64), 64) ;; 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, ".") ;; }