From 87e1c834f75249d25e426c674b65419ccfa41b20 Mon Sep 17 00:00:00 2001 From: Marco Paland Date: Sun, 24 Mar 2019 14:23:13 +0100 Subject: chore(printf, test_suite): clean up --- printf.c | 81 +++++++++++++++++++++++++++++++-------------------- test/test_suite.cpp | 84 +++++++++++++++++++++++++---------------------------- 2 files changed, 89 insertions(+), 76 deletions(-) diff --git a/printf.c b/printf.c index 1770122..c9cebfc 100644 --- a/printf.c +++ b/printf.c @@ -65,16 +65,19 @@ #endif // support for exponential floating point notation (%e/%g) +// default: activated #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL #define PRINTF_SUPPORT_EXPONENTIAL #endif // define the default floating point precision +// default: 6 digits #ifndef PRINTF_DEFAULT_FLOAT_PRECISION #define PRINTF_DEFAULT_FLOAT_PRECISION 6U #endif // define the largest float suitable to print with %f +// default: 1e9 #ifndef PRINTF_MAX_FLOAT #define PRINTF_MAX_FLOAT 1e9 #endif @@ -185,11 +188,12 @@ static unsigned int _atoi(const char** str) return i; } + // output the specified string in reverse, taking care of any zero-padding static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) { const size_t start_idx = idx; - + // pad spaces up to given width if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { for (size_t i = len; i < width; i++) { @@ -198,7 +202,9 @@ static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen } // reverse string - while (len) out(buf[--len], buffer, idx++, maxlen); + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } // append pad spaces up to given width if (flags & FLAGS_LEFT) { @@ -206,10 +212,11 @@ static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen out(' ', buffer, idx++, maxlen); } } - + return idx; } + // internal itoa format static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) { @@ -321,6 +328,7 @@ static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); #endif + // internal ftoa for fixed decimal floating point static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) { @@ -341,7 +349,7 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d // test for very large values // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad - if ((value > PRINTF_MAX_FLOAT)||(value < -PRINTF_MAX_FLOAT)) { + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { #if defined(PRINTF_SUPPORT_EXPONENTIAL) return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); #else @@ -447,70 +455,78 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); } + #if defined(PRINTF_SUPPORT_EXPONENTIAL) -// internal ftoa variant for exponential floating-point type -// contributed by Martijn Jasperse +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) { - // check for special values - if ((value != value)||(value > DBL_MAX)||(value < -DBL_MAX)) + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); - + } + // determine the sign - bool negative = value < 0; - if (negative) value = -value; + const bool negative = value < 0; + if (negative) { + value = -value; + } // default precision if (!(flags & FLAGS_PRECISION)) { prec = PRINTF_DEFAULT_FLOAT_PRECISION; } - + // determine the decimal exponent // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) union { uint64_t U; - double F; + double F; } conv; + conv.F = value; - int exp2 = (int)((conv.U >> 52) & 0x07FF) - 1023; // effectively log2 - conv.U = (conv.U & ((1ULL << 52) - 1)) | (1023ULL << 52); // drop the exponent so conv.F is now in [1,2) + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 - int expval = (int)(0.1760912590558 + exp2*0.301029995663981 + (conv.F - 1.5)*0.289529654602168); + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); // now we want to compute 10^expval but we want to be sure it won't overflow - exp2 = (int)(expval*3.321928094887362 + 0.5); - double z = expval*2.302585092994046 - exp2*0.6931471805599453; - double z2 = z*z; - conv.U = (uint64_t)(exp2 + 1023) << 52; - // compute exp(z) using continued fractions - // https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex - conv.F *= 1 + 2*z/(2 - z + (z2/(6 + (z2/(10 + z2/14))))); + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); // correct for rounding errors if (value < conv.F) { expval--; conv.F /= 10; } - + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters unsigned int minwidth = ((expval < 100)&&(expval > -100)) ? 4 : 5; - + // in "%g" mode, "prec" is the number of *significant figures* not decimals if (flags & FLAGS_ADAPT_EXP) { // do we want to fall-back to "%f" mode? - if ((value >= 1e-4)&&(value < 1e6)) { + if ((value >= 1e-4) && (value < 1e6)) { if ((int)prec > expval) { prec = (unsigned)((int)prec - expval - 1); - } else { + } + else { prec = 0; } - flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision // no characters in exponent minwidth = 0; expval = 0; - } else { + } + else { // we use one sigfig for the whole part - if ((prec > 0)&&(flags & FLAGS_PRECISION)) --prec; + if ((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } } } + // will everything fit? unsigned int fwidth = width; if (width > minwidth) { @@ -526,7 +542,9 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d } // rescale the float value - if (expval) value /= conv.F; + if (expval) { + value /= conv.F; + } // output the floating part const size_t start_idx = idx; @@ -545,7 +563,6 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d } return idx; } - #endif // PRINTF_SUPPORT_EXPONENTIAL #endif // PRINTF_SUPPORT_FLOAT diff --git a/test/test_suite.cpp b/test/test_suite.cpp index a113db0..d76a07d 100644 --- a/test/test_suite.cpp +++ b/test/test_suite.cpp @@ -102,6 +102,7 @@ static void vsnprintf_builder_3(char* buffer, ...) va_end(args); } + TEST_CASE("vsnprintf", "[]" ) { char buffer[100]; @@ -346,13 +347,12 @@ TEST_CASE("- flag", "[]" ) { test::sprintf(buffer, "%0-15d", -42); REQUIRE(!strcmp(buffer, "-42 ")); - + test::sprintf(buffer, "%0-15.3e", -42.); REQUIRE(!strcmp(buffer, "-4.200e+01 ")); - + test::sprintf(buffer, "%0-15.3g", -42.); REQUIRE(!strcmp(buffer, "-42.0 ")); - } @@ -876,89 +876,86 @@ TEST_CASE("padding 20.5", "[]" ) { REQUIRE(!strcmp(buffer, " 00EDCB5433")); } + TEST_CASE("padding neg numbers", "[]" ) { char buffer[100]; - + // space padding - test::sprintf(buffer, "% 1d", -5); REQUIRE(!strcmp(buffer, "-5")); - + test::sprintf(buffer, "% 2d", -5); REQUIRE(!strcmp(buffer, "-5")); - + test::sprintf(buffer, "% 3d", -5); REQUIRE(!strcmp(buffer, " -5")); - + test::sprintf(buffer, "% 4d", -5); REQUIRE(!strcmp(buffer, " -5")); - + // zero padding - test::sprintf(buffer, "%01d", -5); REQUIRE(!strcmp(buffer, "-5")); - + test::sprintf(buffer, "%02d", -5); REQUIRE(!strcmp(buffer, "-5")); - + test::sprintf(buffer, "%03d", -5); REQUIRE(!strcmp(buffer, "-05")); - + test::sprintf(buffer, "%04d", -5); REQUIRE(!strcmp(buffer, "-005")); } + TEST_CASE("float padding neg numbers", "[]" ) { char buffer[100]; // space padding - test::sprintf(buffer, "% 3.1f", -5.); REQUIRE(!strcmp(buffer, "-5.0")); - + test::sprintf(buffer, "% 4.1f", -5.); REQUIRE(!strcmp(buffer, "-5.0")); - + test::sprintf(buffer, "% 5.1f", -5.); REQUIRE(!strcmp(buffer, " -5.0")); - + test::sprintf(buffer, "% 6.1g", -5.); REQUIRE(!strcmp(buffer, " -5")); - + test::sprintf(buffer, "% 6.1e", -5.); REQUIRE(!strcmp(buffer, "-5.0e+00")); - + test::sprintf(buffer, "% 10.1e", -5.); REQUIRE(!strcmp(buffer, " -5.0e+00")); - + // zero padding - test::sprintf(buffer, "%03.1f", -5.); REQUIRE(!strcmp(buffer, "-5.0")); - + test::sprintf(buffer, "%04.1f", -5.); REQUIRE(!strcmp(buffer, "-5.0")); - + test::sprintf(buffer, "%05.1f", -5.); REQUIRE(!strcmp(buffer, "-05.0")); - + test::sprintf(buffer, "%010.1e", -5.); REQUIRE(!strcmp(buffer, "-005.0e+00")); - + // zero padding no decimal point - test::sprintf(buffer, "%01.0f", -5.); REQUIRE(!strcmp(buffer, "-5")); - + test::sprintf(buffer, "%02.0f", -5.); REQUIRE(!strcmp(buffer, "-5")); - + test::sprintf(buffer, "%03.0f", -5.); REQUIRE(!strcmp(buffer, "-05")); - + test::sprintf(buffer, "%07.0E", -5.); REQUIRE(!strcmp(buffer, "-05E+00")); - + test::sprintf(buffer, "%03.0g", -5.); REQUIRE(!strcmp(buffer, "-05")); } @@ -1061,7 +1058,7 @@ TEST_CASE("float", "[]" ) { test::sprintf(buffer, "%+8e", INFINITY); REQUIRE(!strcmp(buffer, " +inf")); - + test::sprintf(buffer, "%.4f", 3.1415354); REQUIRE(!strcmp(buffer, "3.1415")); @@ -1143,34 +1140,34 @@ TEST_CASE("float", "[]" ) { test::sprintf(buffer, "%G", 12345.678); REQUIRE(!strcmp(buffer, "12345.7")); - + test::sprintf(buffer, "%.7G", 12345.678); REQUIRE(!strcmp(buffer, "12345.68")); - + test::sprintf(buffer, "%.5G", 123456789.); REQUIRE(!strcmp(buffer, "1.2346E+08")); - + test::sprintf(buffer, "%.6G", 12345.); REQUIRE(!strcmp(buffer, "12345.0")); - + test::sprintf(buffer, "%+12.4g", 123456789.); REQUIRE(!strcmp(buffer, " +1.235e+08")); - + test::sprintf(buffer, "%.2G", 0.001234); REQUIRE(!strcmp(buffer, "0.0012")); - + test::sprintf(buffer, "%+10.4G", 0.001234); REQUIRE(!strcmp(buffer, " +0.001234")); - + test::sprintf(buffer, "%+012.4g", 0.00001234); REQUIRE(!strcmp(buffer, "+001.234e-05")); - + test::sprintf(buffer, "%.3g", -1.2345e-308); REQUIRE(!strcmp(buffer, "-1.23e-308")); - + test::sprintf(buffer, "%+.3E", 1.23e+308); REQUIRE(!strcmp(buffer, "+1.230E+308")); - + // out of range for float: should switch to exp notation test::sprintf(buffer, "%.1f", 1E20); REQUIRE(!strcmp(buffer, "1.0e+20")); @@ -1291,7 +1288,6 @@ TEST_CASE("types", "[]" ) { test::sprintf(buffer, "%ji", -2147483647LL); REQUIRE(!strcmp(buffer, "-2147483647")); } - } @@ -1429,10 +1425,10 @@ TEST_CASE("misc", "[]" ) { test::sprintf(buffer, "%.*g", 2, 0.33333333); REQUIRE(!strcmp(buffer, "0.33")); - + test::sprintf(buffer, "%.*e", 2, 0.33333333); REQUIRE(!strcmp(buffer, "3.33e-01")); - + test::sprintf(buffer, "%.*d", -1, 1); REQUIRE(!strcmp(buffer, "1")); -- cgit v1.2.3