From e6b5331a36a5815cc36a9b2872f29234efee72cb Mon Sep 17 00:00:00 2001 From: Marco Paland Date: Tue, 21 Aug 2018 12:37:46 +0200 Subject: fix(printf): fix floating point precision limit Return the correct count of precision digits now. Fixes #22 --- README.md | 8 +++++++- printf.c | 18 +++++++++++------- test/test_suite.cpp | 12 +++++++++++- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 930207f..eb360fa 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Therefore I decided to write an own, final implementation which meets the follow - Support of decimal/floating number representation (with an own fast itoa/ftoa) - Reentrant and thread-safe, malloc free, no static vars/buffers - LINT and compiler L4 warning free, mature, coverity clean, automotive ready - - Extensive test suite (> 320 test cases) passing + - Extensive test suite (> 330 test cases) passing - Simply the best *printf* around the net - MIT license @@ -167,6 +167,12 @@ int length = sprintf(NULL, "Hello, world"); // length is set to 12 | PRINTF_SUPPORT_PTRDIFF_T | defined | Define this to enable ptrdiff_t (%t) support | +## Caveats +- The internal floating point conversion has a maximum precision of 9 digits. Any higher precision is truncated after the 9th digit and zeros are returned. + So `printf("%.12f", 42.89522312345678)` gives `42.895223123000`. +- Exponential floating point format (e.g. `"%.10e"` to get `1.167e+65`) for large numbers is not supported yet. Sorry. + + ## Test suite For testing just compile, build and run the test suite located in `test/test_suite.cpp`. This uses the [catch](https://github.com/catchorg/Catch2) framework for unit-tests, which is auto-adding main(). Running with the `--wait-for-keypress exit` option waits for the enter key after test end. diff --git a/printf.c b/printf.c index 0fd6569..c28cf13 100644 --- a/printf.c +++ b/printf.c @@ -277,13 +277,14 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d value = 0 - value; } - // limit precision + // set default precision to 6, if not set explicitly if (!(flags & FLAGS_PRECISION)) { - prec = 6U; // by default, precesion is 6 + prec = 6U; } - if (prec > 9U) { - // precision of >= 10 can lead to overflow errors - prec = 9U; + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; } int whole = (int)value; @@ -325,10 +326,13 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d else { unsigned int count = prec; // now do fractional part, as an unsigned number - do { + while (len < PRINTF_FTOA_BUFFER_SIZE) { --count; buf[len++] = (char)(48U + (frac % 10U)); - } while ((len < PRINTF_FTOA_BUFFER_SIZE) && (frac /= 10U)); + if (!(frac /= 10U)) { + break; + } + } // add extra 0s while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { buf[len++] = '0'; diff --git a/test/test_suite.cpp b/test/test_suite.cpp index 3d7c10d..212fbd3 100644 --- a/test/test_suite.cpp +++ b/test/test_suite.cpp @@ -933,7 +933,17 @@ TEST_CASE("float", "[]" ) { REQUIRE(!strcmp(buffer, "42.895200000")); test::sprintf(buffer, "%.10f", 42.895223); - REQUIRE(!strcmp(buffer, "42.895223000")); + REQUIRE(!strcmp(buffer, "42.8952230000")); + + // this testcase checks, that the precision is truncated to 9 digits. + // a perfect working float should return the whole number + test::sprintf(buffer, "%.12f", 42.89522312345678); + REQUIRE(!strcmp(buffer, "42.895223123000")); + + // this testcase checks, that the precision is truncated AND rounded to 9 digits. + // a perfect working float should return the whole number + test::sprintf(buffer, "%.12f", 42.89522387654321); + REQUIRE(!strcmp(buffer, "42.895223877000")); test::sprintf(buffer, "%6.2f", 42.8952); REQUIRE(!strcmp(buffer, " 42.90")); -- cgit v1.2.3