diff --git a/README.md b/README.md index e90bfeb..2141a26 100644 --- a/README.md +++ b/README.md @@ -144,8 +144,9 @@ Like `printf`, `nanoprintf` expects a conversion specification string of the fol * `p`: Pointers * `n`: Write the number of bytes written to the pointer vararg * `f`/`F`: Floating-point decimal - * `e`/`E`: Floating-point scientific (unimplemented, prints decimal) - * `a`/`A`: Floating-point hex (unimplemented, prints decimal) + * `e`/`E`: Floating-point scientific (unimplemented, prints float decimal) + * `g`/`G`: Floating-point shortest (unimplemented, prints float decimal) + * `a`/`A`: Floating-point hex (unimplemented, prints float decimal) * `b`/`B`: Binary integers ## Floating Point @@ -154,7 +155,7 @@ Floating point conversion is performed by extracting the value into 64:64 fixed- Because the float -> fixed code operates on the raw float value bits, no floating point operations are performed. This allows nanoprintf to efficiently format floats on soft-float architectures like Cortex-M0, and to function identically with or without optimizations like "fast math". Despite `nano` in the name, there's no way to do away with double entirely, since the C language standard says that floats are promoted to double any time they're passed into variadic argument lists. nanoprintf casts all doubles back down to floats before doing any conversions. No other single- or double- precision operations are performed. -The `%e`/`%E` and `%a`/`%A` specifiers are parsed but not formatted. If used, the output will be identical to if `%f`/`%F` was used. Pull requests welcome! :) +The `%e`/`%E`, `%a`/`%A`, and `%g`/`%G` specifiers are parsed but not formatted. If used, the output will be identical to if `%f`/`%F` was used. Pull requests welcome! :) ## Limitations diff --git a/nanoprintf.h b/nanoprintf.h index cd7ba61..3bd1d20 100644 --- a/nanoprintf.h +++ b/nanoprintf.h @@ -210,9 +210,10 @@ typedef enum { NPF_FMT_SPEC_CONV_WRITEBACK, // 'n' #endif #if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 - NPF_FMT_SPEC_CONV_FLOAT_DEC, // 'f', 'F' - NPF_FMT_SPEC_CONV_FLOAT_SCI, // 'e', 'E' - NPF_FMT_SPEC_CONV_FLOAT_HEX, // 'a', 'A' + NPF_FMT_SPEC_CONV_FLOAT_DEC, // 'f', 'F' + NPF_FMT_SPEC_CONV_FLOAT_SCI, // 'e', 'E' + NPF_FMT_SPEC_CONV_FLOAT_SHORTEST, // 'g', 'G' + NPF_FMT_SPEC_CONV_FLOAT_HEX, // 'a', 'A' #endif } npf_format_spec_conversion_t; @@ -447,6 +448,13 @@ int npf_parse_format_spec(char const *format, npf_format_spec_t *out_spec) { if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } break; + case 'G': + out_spec->case_adjust = 0; + case 'g': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SHORTEST; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } + break; + case 'A': out_spec->case_adjust = 0; case 'a': @@ -878,6 +886,7 @@ int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) { #if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 case NPF_FMT_SPEC_CONV_FLOAT_DEC: case NPF_FMT_SPEC_CONV_FLOAT_SCI: + case NPF_FMT_SPEC_CONV_FLOAT_SHORTEST: case NPF_FMT_SPEC_CONV_FLOAT_HEX: { float val; if (fs.length_modifier == NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE) { diff --git a/tests/unit_parse_format_spec.cc b/tests/unit_parse_format_spec.cc index 9ad8ceb..fc62999 100644 --- a/tests/unit_parse_format_spec.cc +++ b/tests/unit_parse_format_spec.cc @@ -452,6 +452,18 @@ TEST_CASE("npf_parse_format_spec") { REQUIRE(spec.case_adjust == 0); } + SUBCASE("g") { + REQUIRE(npf_parse_format_spec("%g", &spec) == 2); + REQUIRE(spec.conv_spec == NPF_FMT_SPEC_CONV_FLOAT_SHORTEST); + REQUIRE(spec.case_adjust == 'a' - 'A'); + } + + SUBCASE("G") { + REQUIRE(npf_parse_format_spec("%G", &spec) == 2); + REQUIRE(spec.conv_spec == NPF_FMT_SPEC_CONV_FLOAT_SHORTEST); + REQUIRE(spec.case_adjust == 0); + } + SUBCASE("a") { REQUIRE(npf_parse_format_spec("%a", &spec) == 2); REQUIRE(spec.conv_spec == NPF_FMT_SPEC_CONV_FLOAT_HEX); @@ -473,42 +485,3 @@ TEST_CASE("npf_parse_format_spec") { } } -/* - Not implemented yet - -TEST(npf_parse_format_spec, e) { - CHECK_EQUAL(2, npf_parse_format_spec("%e", &spec)); - CHECK_EQUAL(NPF_FMT_SPEC_CONV_FLOAT_EXPONENT, spec.conv_spec); - CHECK_EQUAL(NPF_FMT_SPEC_CONV_CASE_LOWER, spec.conv_spec_case); -} - -TEST(npf_parse_format_spec, E) { - CHECK_EQUAL(2, npf_parse_format_spec("%E", &spec)); - CHECK_EQUAL(NPF_FMT_SPEC_CONV_FLOAT_EXPONENT, spec.conv_spec); - CHECK_EQUAL(NPF_FMT_SPEC_CONV_CASE_UPPER, spec.conv_spec_case); -} - -TEST(npf_parse_format_spec, a) { - CHECK_EQUAL(2, npf_parse_format_spec("%a", &spec)); - CHECK_EQUAL(NPF_FMT_SPEC_CONV_FLOAT_HEXPONENT, spec.conv_spec); - CHECK_EQUAL(NPF_FMT_SPEC_CONV_CASE_LOWER, spec.conv_spec_case); -} - -TEST(npf_parse_format_spec, A) { - CHECK_EQUAL(2, npf_parse_format_spec("%A", &spec)); - CHECK_EQUAL(NPF_FMT_SPEC_CONV_FLOAT_HEXPONENT, spec.conv_spec); - CHECK_EQUAL(NPF_FMT_SPEC_CONV_CASE_UPPER, spec.conv_spec_case); -} - -TEST(npf_parse_format_spec, g) { - CHECK_EQUAL(2, npf_parse_format_spec("%g", &spec)); - CHECK_EQUAL(NPF_FMT_SPEC_CONV_FLOAT_DYNAMIC, spec.conv_spec); - CHECK_EQUAL(NPF_FMT_SPEC_CONV_CASE_LOWER, spec.conv_spec_case); -} - -TEST(npf_parse_format_spec, G) { - CHECK_EQUAL(2, npf_parse_format_spec("%G", &spec)); - CHECK_EQUAL(NPF_FMT_SPEC_CONV_FLOAT_DYNAMIC, spec.conv_spec); - CHECK_EQUAL(NPF_FMT_SPEC_CONV_CASE_UPPER, spec.conv_spec_case); -} -*/