shithub: mc

ref: 7f941fe7689b6085a18d67389eb7cd7f8b3fae48
dir: /libstd/fltfmt.myr/

View raw version
use "bigint.use"

pkg std =
	const float64bfmt	: 
	const float32bfmt	: 

const Dblbias = 1023
const Fltbias

const fltbfmt = {buf, dbl
	var sign, exp, mant

	(sign, mant, exp) = float64explode(dbl)
	-> dragon4(buf, sign, mant, (exp - 52) castto(int64), DblBias, `Normal, -32)
}

type cutmode = union
	`Normal
	`Absolute
	`Relative
;;

/*
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

	put("encoding... isneg = %t, f = %xl, exp = %l, p = %l\n", isneg, f, e, p)
	/* if we have zero for the mantissa, we can return early */
	n = 0
	if isneg
		n += bfmt(buf, "-")
	;;
	if f == 0
		n += bfmt(buf, "0.0")
		-> buf[: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)
	put("r = %s, s = %s, mm = %s, mp = %s\n", \
		bigfmt(r, 0), bigfmt(s, 0), bigfmt(mm, 0), bigfmt(mp, 0))

	/* 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)

	put("r = %s, s = %s, mm = %s, mp = %s\n", \
		bigfmt(r, 0), bigfmt(s, 0), bigfmt(mm, 0), bigfmt(mp, 0))

	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)
	;;
	put("r = %s, s = %s, mm = %s, mp = %s\n", \
		bigfmt(r, 0), bigfmt(s, 0), bigfmt(mm, 0), bigfmt(mp, 0))

	t = bigdup(r)
	bigshli(t, 1)
	bigadd(t, mp)
	while true
		u = bigdup(s)
		bigshli(u, 1)
		put("t = 2*r + mp = %s, u = 2*s = %s\n", bigfmt(t, 0), bigfmt(u, 0))
		put("s = %s\n", bigfmt(s, 0))
		match bigcmp(t, u)
		| `Before:
			bigfree(u)
			break
		| _:
			k++
			bigmuli(s, 10)
			bigfree(u)
		;;
	;;
	cutoff = k - buf.len - 1
	bigfree(t)
	put("r = %s, s = %s, mm = %s, mp = %s, cutoff = %i\n", \
		bigfmt(r, 0), bigfmt(s, 0), bigfmt(mm, 0), bigfmt(mp, 0), \ 
		cutoff)

	if k <= 0
		n += bfmt(buf[n:], "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)
		put("t = %s, mm = %s\n", bigfmt(t, 0), bigfmt(mm, 0))
		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
			put("low = %t, high = %t, k == cutoff = %t\n", \
				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)
		;;
	;;
	put("n = %i\n", n)
	-> buf[: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
}