commit 5a1eed1cb0227e1dba686ef16a97baf7ac566057
parent c96533b97d7287ce978702dc73d3962195e38932
Author: Ember Sawady <ecs@d2evs.net>
Date: Mon, 6 Nov 2023 21:08:01 +0000
fmt: flesh out float formatting
Signed-off-by: Ember Sawady <ecs@d2evs.net>
Diffstat:
4 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/fmt/+test.ha b/fmt/+test.ha
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
+use math;
use strconv;
@test fn print() void = {
@@ -66,6 +67,15 @@ use strconv;
assert(bsprintf(buf, "{:.1}", 123.0) == "100");
assert(bsprintf(buf, "{:.5}", 123.0) == "123");
+ assert(bsprintf(buf, "{:f}", 1.0e4) == "10000");
+ assert(bsprintf(buf, "{:e}", 123.45) == "1.2345e2");
+ assert(bsprintf(buf, "{:Fs}", 1.0) == "+1");
+ assert(bsprintf(buf, "{:F.}", 1.0) == "1.0");
+ assert(bsprintf(buf, "{:FU}", math::INF) == "INFINITY");
+ assert(bsprintf(buf, "{:FE}", 1.0e4) == "1E4");
+ assert(bsprintf(buf, "{:FS}", 1.0e4) == "1e+4");
+ assert(bsprintf(buf, "{:F2}", 1.0e4) == "1e04");
+
assert(bsprintf(buf, "{:=5}", "hi") == " hi ");
assert(bsprintf(buf, "{:=6}", "hi") == " hi ");
diff --git a/fmt/README b/fmt/README
@@ -38,6 +38,17 @@ A format modifier can be any of the following:
- "X": Format numbers in uppercase hexadecimal.
- "o": Format numbers in octal.
- "b": Format numbers in binary.
+- "e": Format floats in scientific notation.
+- "f": Format floats in fixed-point notation.
+- "g": Format floats in whichever of scientific and fixed-point notation is
+ shortest. This is the default.
+- "F" followed by "s": Use a sign for both positive and negative numbers.
+- "F" followed by ".": Always include at least one digit after the decimal
+ point.
+- "F" followed by "U": Uppercase INFINITY and NAN.
+- "F" followed by "E": Uppercase exponent symbols (E and P rather than e and p).
+- "F" followed by "S": Use a sign for both positive and negative exponents.
+- "F" followed by "2": Show at least two digits of the exponent.
- "." followed by a number N: Sets the precision to N. Integers will be
left-padded with "0"s between the sign and the number itself. Strings
will be truncated to N runes. Floats will only include up to N digits after
diff --git a/fmt/iter.ha b/fmt/iter.ha
@@ -37,6 +37,8 @@ export type mods = struct {
width: size,
prec: size,
base: strconv::base,
+ ffmt: strconv::ffmt,
+ fflags: strconv::fflags,
};
type iterator = struct {
@@ -142,6 +144,21 @@ fn scan_modifiers(it: *iterator, mod: *mods) void = {
case 'X' => mod.base = strconv::base::HEX_UPPER;
case 'o' => mod.base = strconv::base::OCT;
case 'b' => mod.base = strconv::base::BIN;
+ // ffmt
+ case 'e' => mod.ffmt = strconv::ffmt::E;
+ case 'f' => mod.ffmt = strconv::ffmt::F;
+ case 'g' => mod.ffmt = strconv::ffmt::G;
+ // fflags
+ case 'F' =>
+ switch (getrune(it)) {
+ case 's' => mod.fflags |= strconv::fflags::SHOW_POS;
+ case '.' => mod.fflags |= strconv::fflags::SHOW_POINT;
+ case 'U' => mod.fflags |= strconv::fflags::UPPERCASE;
+ case 'E' => mod.fflags |= strconv::fflags::UPPER_EXP;
+ case 'S' => mod.fflags |= strconv::fflags::SHOW_POS_EXP;
+ case '2' => mod.fflags |= strconv::fflags::SHOW_TWO_EXP_DIGITS;
+ case => abort("Invalid float flag");
+ };
// precision
case '.' => mod.prec = scan_sz(it);
// width
diff --git a/fmt/print.ha b/fmt/print.ha
@@ -106,8 +106,8 @@ case let f: types::floating =>
|| mod.base == strconv::base::DEC); // TODO
assert(mod.prec <= types::UINT_MAX);
// TODO: mod.prec should be (size | void) but that needs @default
- return strconv::fftosf(out, f, strconv::ffmt::G,
- if (mod.prec != 0) mod.prec: uint else void, 0)?;
+ return strconv::fftosf(out, f, mod.ffmt,
+ if (mod.prec != 0) mod.prec: uint else void, mod.fflags)?;
case let i: types::integer =>
let neg = match (i) {
case types::unsigned =>