Eigenstate: myrddin-dev mailing list

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 2/5] Allow padding for fltXY formatting


---
 lib/std/fltfmt.myr      | 86 +++++++++++++++++++++++++++++--------
 lib/std/fmt.myr         |  5 +++
 lib/std/test/fltfmt.myr | 95 +++++++++++++++++++++++++++++++++++++----
 3 files changed, 160 insertions(+), 26 deletions(-)

diff --git a/lib/std/fltfmt.myr b/lib/std/fltfmt.myr
index ee035520..a215c12a 100644
--- a/lib/std/fltfmt.myr
+++ b/lib/std/fltfmt.myr
@@ -3,6 +3,7 @@ use "bigint"
 use "die"
 use "extremum"
 use "fltbits"
+use "option"
 use "slpush"
 use "strbuf"
 use "types"
@@ -17,6 +18,8 @@ pkg std =
 	pkglocal type fltparams = struct
 		mode	: int
 		prec	: int
+		padto	: size
+		padfill	: char
 	;;
 
 	pkglocal const flt64bfmt	: (sb : strbuf#, opts : fltparams, val : flt64 -> void)
@@ -30,46 +33,93 @@ const flt64bfmt = {sb, opts, val
 	var isneg, exp, mant
 
 	if isnan(val)
-		sbputs(sb, "NaN")
-		-> void
+		-> nanfmt(sb, opts)
 	;;
 
 	(isneg, exp, mant) = flt64explode(val)
 
 	if exp > Dblbias
-		if isneg
-			std.sbputs(sb, "-Inf")
-		else
-			std.sbputs(sb, "Inf")
-		;;
-		-> void
+		-> inffmt(sb, opts, isneg)
 	;;
 
+	var valsb = mksb()
+
 	exp = max(exp, 1 - Dblbias)
-	dragon4(sb, isneg, mant, exp - 52, Dblbias, opts.mode, opts.prec)
+	dragon4(valsb, false, mant, exp - 52, Dblbias, opts.mode, opts.prec)
+
+	-> blobfmt(sb, sbfin(valsb), opts, isneg)
 }
 
 const flt32bfmt = {sb, opts, val
 	var isneg, exp, mant
 
 	if isnan(val)
-		sbputs(sb, "NaN")
-		-> void
+		-> nanfmt(sb, opts)
 	;;
 
 	(isneg, exp, mant) = flt32explode(val)
 
 	if (exp : int64) > Fltbias
-		if isneg
-			std.sbputs(sb, "-Inf")
-		else
-			std.sbputs(sb, "Inf")
-		;;
-		-> void
+		-> inffmt(sb, opts, isneg)
 	;;
 
+	var valsb = mksb()
+
 	exp = (max((exp : int64), 1 - Fltbias) : int32)
-	dragon4(sb, isneg, (mant : uint64), (exp - 23 : int64), Fltbias, opts.mode, opts.prec)
+	dragon4(valsb, false, (mant : uint64), (exp - 23 : int64), Fltbias, opts.mode, opts.prec)
+
+	-> 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)
 }
 
 /*
diff --git a/lib/std/fmt.myr b/lib/std/fmt.myr
index 540d6d8e..51dc36ef 100644
--- a/lib/std/fmt.myr
+++ b/lib/std/fmt.myr
@@ -454,10 +454,14 @@ const fltparams = {params
 	fp = [
 		.mode = MNormal,
 		.prec = 0,
+		.padfill = ' ',
+		.padto = 0,
 	]
 
 	for p : params
 		match p
+		| ("w", wid):	fp.padto = getint(wid, "fmt: width must be integer")
+		| ("p", pad):	fp.padfill = decode(pad)
 		| (opt, arg):
 			std.write(2, "fmt: ")
 			std.write(2, opt)
@@ -466,6 +470,7 @@ const fltparams = {params
 			std.die("\nunreachable\n")
 		;;
 	;;
+	iassert(fp.padto >= 0, "pad must be >= 0")
 	-> fp
 }
 
diff --git a/lib/std/test/fltfmt.myr b/lib/std/test/fltfmt.myr
index 349f8bc2..7670fff8 100644
--- a/lib/std/test/fltfmt.myr
+++ b/lib/std/test/fltfmt.myr
@@ -8,21 +8,24 @@ const main = {
 		[.name = "put0", .fn = put0],
 		[.name = "putNaN", .fn = putNaN],
 		[.name = "putInf", .fn = putInf],
+		[.name = "putinteger", .fn = putinteger],
+		[.name = "putlowprec", .fn = putlowprec],
+		[.name = "padding", .fn = padding],
 	][:])
 }
 
 const put0 = {c
-        var f : flt32 = 0.0
-        var g : flt64 = 0.0
+	var f : flt32 = 0.0
+	var g : flt64 = 0.0
 	testr.check(c, std.eq(std.fmt("f is {}", f), "f is 0.0"), "0.0 should format to \"0.0\"")
 	testr.check(c, std.eq(std.fmt("g is {}", g), "g is 0.0"), "0.0 should format to \"0.0\"")
 }
 
 const putNaN = {c
-        var f : flt32 = std.flt32nan()
-        var g : flt64 = std.flt64nan()
-        var f2 : flt32 = std.flt32frombits(0x7fc00ab0)
-        var g2 : flt64 = std.flt64frombits(0x7ff800000000abc0)
+	var f : flt32 = std.flt32nan()
+	var g : flt64 = std.flt64nan()
+	var f2 : flt32 = std.flt32frombits(0x7fc00ab0)
+	var g2 : flt64 = std.flt64frombits(0x7ff800000000abc0)
 	testr.check(c, std.eq(std.fmt("f is {}", f), "f is NaN"), "NaN should format to \"NaN\"")
 	testr.check(c, std.eq(std.fmt("g is {}", g), "g is NaN"), "NaN should format to \"NaN\"")
 	testr.check(c, std.eq(std.fmt("f2 is {}", f2), "f2 is NaN"), "alt NaN should format to \"NaN\"")
@@ -30,9 +33,85 @@ const putNaN = {c
 }
 
 const putInf = {c
-        var f : flt32 = std.flt32inf()
-        var g : flt64 = std.flt64inf()
+	var f : flt32 = std.flt32inf()
+	var g : flt64 = std.flt64inf()
 	testr.check(c, std.eq(std.fmt("f is {}", f), "f is Inf"), "Inf should format to \"Inf\"")
 	testr.check(c, std.eq(std.fmt("g is {}", g), "g is Inf"), "Inf should format to \"Inf\"")
+	f *= -1.0
+	g *= -1.0
+	testr.check(c, std.eq(std.fmt("f is {}", f), "f is -Inf"), "-Inf should format to \"-Inf\"")
+	testr.check(c, std.eq(std.fmt("g is {}", g), "g is -Inf"), "-Inf should format to \"-Inf\"")
 }
 
+const putinteger = {c
+	var data : (int, byte[:])[:] = [
+		(1, "1.0"), (2, "2.0"), (184, "184.0"), (-37, "-37.0")
+	][:]
+
+	for (f, exp) : data
+		var f32 : flt32 = (f : flt32)
+		var f64 : flt64 = (f : flt64)
+		var act32 : byte[:] = std.fmt("{}", f32)
+		var act64 : byte[:] = std.fmt("{}", f64)
+
+		testr.check(c, std.eq(act32, exp), "{} should format [flt32] to \"{}\", was \"{}\"", f, exp, act32)
+		testr.check(c, std.eq(act64, exp), "{} should format [flt64] to \"{}\", was \"{}\"", f, exp, act64)
+	;;
+}
+
+const putlowprec = {c
+	var data : (flt64, byte[:])[:] = [
+		(1.5, "1.5"), (0.0025, "0.0025"), (4.25, "4.25"), (-229.25, "-229.25")
+	][:]
+
+	for (f, exp) : data
+		var f32 : flt32 = (f : flt32)
+		var f64 : flt64 = (f : flt64)
+		var act32 : byte[:] = std.fmt("{}", f32)
+		var act64 : byte[:] = std.fmt("{}", f64)
+
+		testr.check(c, std.eq(act32, exp), "{} should format [flt32] to \"{}\", was \"{}\"", f, exp, act32)
+		testr.check(c, std.eq(act64, exp), "{} should format [flt64] to \"{}\", was \"{}\"", f, exp, act64)
+	;;
+}
+
+const padding = {c
+	var exp, act
+	var f32 : flt32
+	var f64 : flt64
+
+	f32 = 1.0
+	exp = "XXXX1.0"
+	act = std.fmt("{w=7,p=X}", f32)
+	testr.check(c, std.eq(exp, act), "{} should format [flt32] to \"{}\", was \"{}\"", f32, exp, act)
+
+	f64 = 1.0
+	exp = "XXXX1.0"
+	act = std.fmt("{w=7,p=X}", f64)
+	testr.check(c, std.eq(exp, act), "{} should format [flt32] to \"{}\", was \"{}\"", f64, exp, act)
+
+	f32 = -2.5
+	exp = "YYYYYY-2.5"
+	act = std.fmt("{w=10,p=Y}", f32)
+	testr.check(c, std.eq(exp, act), "{} should format [flt32] to \"{}\", was \"{}\"", f32, exp, act)
+
+	f64 = -100000.5
+	exp = "-100000.5"
+	act = std.fmt("{w=9,p=Y}", f64)
+	testr.check(c, std.eq(exp, act), "{} should format [flt32] to \"{}\", was \"{}\"", f64, exp, act)
+
+	f32 = -13.25
+	exp = "-013.25"
+	act = std.fmt("{w=7,p=0}", f32)
+	testr.check(c, std.eq(exp, act), "{} should format [flt32] to \"{}\", was \"{}\"", f32, exp, act)
+
+	f32 = -1.0 * std.flt32inf()
+	exp = "0000000-Inf"
+	act = std.fmt("{w=11,p=0}", f32)
+	testr.check(c, std.eq(exp, act), "{} should format [flt32] to \"{}\", was \"{}\"", f32, exp, act)
+
+	f64 = std.flt64nan()
+	exp = "               NaN"
+	act = std.fmt("{w=18,p= }", f64)
+	testr.check(c, std.eq(exp, act), "{} should format [flt32] to \"{}\", was \"{}\"", f64, exp, act)
+}
-- 
2.23.0


References:
[PATCH 0/5] Allow printing floating point in scientific notation"S. Gilles" <sgilles@xxxxxxxxxxxx>