From 5368d702d44e3ac6759793d2f6c882eefe12cff3 Mon Sep 17 00:00:00 2001 From: Okarss <104319900+Okarss@users.noreply.github.com> Date: Mon, 10 Jun 2024 01:34:12 +0300 Subject: [PATCH] Change the error text and retarget the submodule to main --- README.md | 4 ++-- nanoprintf.h | 18 +++++++++--------- tests/mpaland-conformance | 2 +- tests/unit_ftoa_rev.cc | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 73a5358..a4b393a 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ If a disabled format specifier feature is used, no conversion will occur and the ### Floating-Point Conversion nanoprintf has the following floating-point specific configuration defines. -* `NANOPRINTF_CONVERSION_BUFFER_SIZE`: Optional, defaults to `23`. Sets the size of a character buffer used for storing the converted value. Set to a larger number to enable printing of floating-point numbers with more characters. The buffer size does include the integer part, the fraction part and the decimal separator, but does not include the sign and the padding characters. If the number does not fit into buffer, an `oor` is printed. Be careful with large sizes as the conversion buffer is allocated on stack memory. +* `NANOPRINTF_CONVERSION_BUFFER_SIZE`: Optional, defaults to `23`. Sets the size of a character buffer used for storing the converted value. Set to a larger number to enable printing of floating-point numbers with more characters. The buffer size does include the integer part, the fraction part and the decimal separator, but does not include the sign and the padding characters. If the number does not fit into buffer, an `err` is printed. Be careful with large sizes as the conversion buffer is allocated on stack memory. * `NANOPRINTF_CONVERSION_FLOAT_TYPE`: Optional, defaults to `unsigned int`. Sets the integer type used for float conversion algorithm, which determines the conversion accuracy. Can be set to any unsigned integer type, like for example `uint64_t` or `uint8_t`. ### Sprintf Safety @@ -160,7 +160,7 @@ Like `printf`, `nanoprintf` expects a conversion specification string of the fol ## Floating-Point -Floating-point conversion is performed by extracting the integer and fraction parts of the number into two separate integer variables and then doing a dynamic scaling to bring those values down or up to the decimal separator. The dynamic scaling multiplies and divides the value by 2 and 10 iteratively to keep the most significant bits of the value. The further the value is away from the decimal separator, the more of an error the scaling will accumulate. With an integer width of `N` bits on average the algorithm keeps `N - log2(5)` or `N - 2.322` bits of accuracy. In addition integer parts up to `2 ^^ N - 1` and fraction parts with up to `N - 2.322` bits after the decimal separator are converted perfectly without loosing any bits. +Floating-point conversion is performed by extracting the integer and fraction parts of the number into two separate integer variables. For each part the exponent is then scaled from base-2 to base-10 by iteratively multiplying and dividing the mantissa by 2 and 5 appropriately. The order of the scaling operations is selected dynamically (depending on value) to retain as much of the most significant bits of the mantissa as possible. The further the value is away from the decimal separator, the more of an error the scaling will accumulate. With a conversion integer type width of `N` bits on average the algorithm retains `N - log2(5)` or `N - 2.322` bits of accuracy. In addition integer parts up to `2 ^^ N - 1` and fraction parts with up to `N - 2.322` bits after the decimal separator are converted perfectly without loosing any bits. 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, to function identically with or without optimizations like "fast math", and to minimize the code footprint. diff --git a/nanoprintf.h b/nanoprintf.h index 8c4bbbd..e9fc971 100644 --- a/nanoprintf.h +++ b/nanoprintf.h @@ -573,7 +573,7 @@ static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, double f) { buf[dec++] = '.'; } - { // integer part + { // Integer part npf_ftoa_man_t man_i; if (exp >= 0) { @@ -586,10 +586,10 @@ static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, double f) { if (shift_i) { carry = (bin >> (shift_i - 1)) & 0x1; } - exp = NPF_DOUBLE_MAN_BITS; // disable the fraction part + exp = NPF_DOUBLE_MAN_BITS; // invalidate the fraction part } - // Scale the integer down to the decimal separator. + // Scale the exponent from base-2 to base-10. for (; exp_i; --exp_i) { if (!(man_i & ((npf_ftoa_man_t)0x1 << (NPF_FTOA_MAN_BITS - 1)))) { man_i = (npf_ftoa_man_t)(man_i << 1); @@ -606,7 +606,7 @@ static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, double f) { } end = dec; - // print the integer + // Print the integer do { if (end >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; } buf[end++] = (char)('0' + (char)(man_i % 10)); @@ -614,7 +614,7 @@ static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, double f) { } while (man_i); } - { // fraction part + { // Fraction part npf_ftoa_man_t man_f; npf_ftoa_dec_t dec_f = (npf_ftoa_dec_t)spec->prec; @@ -632,7 +632,7 @@ static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, double f) { carry = 0; } - // Scale the fraction up to the decimal separator and prepare the first digit. + // Scale the exponent from base-2 to base-10 and prepare the first digit. for (uint_fast8_t digit = 0; dec_f && (exp_f < 4); ++exp_f) { if ((man_f > ((npf_ftoa_man_t)-4 / 5)) || digit) { carry = (uint_fast8_t)(man_f & 0x1); @@ -655,7 +655,7 @@ static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, double f) { } if (dec_f) { - // print the fraction + // Print the fraction for (;;) { buf[--dec_f] = (char)('0' + (char)(man_f >> (NPF_FTOA_MAN_BITS - 4))); man_f = (npf_ftoa_man_t)(man_f & ~((npf_ftoa_man_t)0xF << (NPF_FTOA_MAN_BITS - 4))); @@ -669,7 +669,7 @@ static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, double f) { } } - // round the number + // Round the number for (; carry; ++dec) { if (dec >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; } if (dec >= end) { buf[end++] = '0'; } @@ -680,7 +680,7 @@ static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, double f) { return (int)end; exit: - if (!ret) { ret = "ROO"; } + if (!ret) { ret = "RRE"; } uint_fast8_t i; for (i = 0; ret[i]; ++i) { buf[i] = (char)(ret[i] + spec->case_adjust); } return (int)i; diff --git a/tests/mpaland-conformance b/tests/mpaland-conformance index c133e9d..7b954a3 160000 --- a/tests/mpaland-conformance +++ b/tests/mpaland-conformance @@ -1 +1 @@ -Subproject commit c133e9d72f953d5aa8cac68e72b6b05ab657117a +Subproject commit 7b954a34cdff72b8ed68c0088dd37335d8f11a32 diff --git a/tests/unit_ftoa_rev.cc b/tests/unit_ftoa_rev.cc index 320c5c6..e28a1af 100644 --- a/tests/unit_ftoa_rev.cc +++ b/tests/unit_ftoa_rev.cc @@ -49,13 +49,13 @@ TEST_CASE("ftoa_rev") { require_ftoa_rev("NAN", (double)-NAN); require_ftoa_rev("INF", (double)+INFINITY); require_ftoa_rev("INF", (double)-INFINITY); - require_ftoa_rev("OOR", DBL_MAX); + require_ftoa_rev("ERR", DBL_MAX); spec.prec = NANOPRINTF_CONVERSION_BUFFER_SIZE - 2; - require_ftoa_rev("OOR", 10.); + require_ftoa_rev("ERR", 10.); spec.prec += 1; - require_ftoa_rev("OOR", 9.); + require_ftoa_rev("ERR", 9.); spec.case_adjust = 'a' - 'A'; // lowercase - require_ftoa_rev("oor", 0.); + require_ftoa_rev("err", 0.); } SUBCASE("zero and decimal separator") {