aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarco Paland <marco@paland.com>2019-03-24 14:23:13 +0100
committerMarco Paland <marco@paland.com>2019-03-24 14:23:13 +0100
commit87e1c834f75249d25e426c674b65419ccfa41b20 (patch)
tree440d6a311c620edb84b95b48e131c95953ba05e9
parentd61d0146e6ce9bf13396a015c5dece9eac4bf901 (diff)
downloadprintf-87e1c834f75249d25e426c674b65419ccfa41b20.tar.gz
printf-87e1c834f75249d25e426c674b65419ccfa41b20.tar.bz2
printf-87e1c834f75249d25e426c674b65419ccfa41b20.zip
chore(printf, test_suite): clean up
-rw-r--r--printf.c81
-rw-r--r--test/test_suite.cpp84
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 <m.jasperse@gmail.com>
+// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
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"));