From 8ff74d6000b352ea47ace8895e1bd66146e6086b Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 14 Sep 2017 13:47:04 -0400 Subject: [PATCH 1/4] Allow UnityPrintFloat() to print a 7th digit. --- src/unity.c | 49 +++++++++++++----- test/tests/testunity.c | 114 ++++++++++++++++++++++++----------------- 2 files changed, 102 insertions(+), 61 deletions(-) diff --git a/src/unity.c b/src/unity.c index 0f2d2de..429ff06 100644 --- a/src/unity.c +++ b/src/unity.c @@ -258,11 +258,14 @@ void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number) /*-----------------------------------------------*/ #ifndef UNITY_EXCLUDE_FLOAT_PRINT -/* This function prints a floating-point value in a format similar to - * printf("%.6g"). It can work with either single- or double-precision, - * but for simplicity, it prints only 6 significant digits in either case. - * Printing more than 6 digits accurately is hard (at least in the single- - * precision case) and isn't attempted here. */ +/* + * This function prints a floating-point value in a format similar to + * printf("%.7g"). It can work with either single- or double-precision, + * but for simplicity, it prints only 7 significant digits in either case. + * The 7th digit won't always be totally correct in single-precision + * operation (for that level of accuracy, a more complicated algorithm + * would be needed). + */ void UnityPrintFloat(const UNITY_DOUBLE input_number) { UNITY_DOUBLE number = input_number; @@ -285,22 +288,42 @@ void UnityPrintFloat(const UNITY_DOUBLE input_number) UNITY_INT32 n; char buf[16]; - /* scale up or down by powers of 10 */ - while (number < 100000.0f / 1e6f) { number *= 1e6f; exponent -= 6; } - while (number < 100000.0f) { number *= 10.0f; exponent--; } - while (number > 1000000.0f * 1e6f) { number /= 1e6f; exponent += 6; } - while (number > 1000000.0f) { number /= 10.0f; exponent++; } + /* + * Scale up or down by powers of 10. To minimize rounding error, + * start with a factor/divisor of 10^10, which is the largest + * power of 10 that can be represented exactly. Finally, compute + * (exactly) the remaining power of 10 and perform one more + * multiplication or division. + */ + if(number < 1e6f) + { + UNITY_DOUBLE factor = 1.0f; + + while(number < 1e7f / 1e10f) { number *= 1e10f; exponent -= 10; } + while(number * factor < 1e6f) { factor *= 10.0f; exponent--; } + + number *= factor; + } + else if(number > 1e7f) + { + UNITY_DOUBLE divisor = 1.0f; + + while(number > 1e6f * 1e10f) { number /= 1e10f; exponent += 10; } + while(number / divisor > 1e7f) { divisor *= 10.0f; exponent++; } + + number /= divisor; + } /* round to nearest integer */ n = ((UNITY_INT32)(number + number) + 1) / 2; - if (n > 999999) + if (n > 9999999) { - n = 100000; + n = 1000000; exponent++; } /* determine where to place decimal point */ - decimals = (exponent <= 0 && exponent >= -9) ? -exponent : 5; + decimals = (exponent <= 0 && exponent >= -10) ? -exponent : 6; exponent += decimals; /* truncate trailing zeroes after decimal point */ diff --git a/test/tests/testunity.c b/test/tests/testunity.c index af06647..752c3b2 100644 --- a/test/tests/testunity.c +++ b/test/tests/testunity.c @@ -4466,46 +4466,48 @@ void testFloatPrinting(void) #if defined(UNITY_EXCLUDE_FLOAT_PRINT) || !defined(USING_OUTPUT_SPY) TEST_IGNORE(); #else + /* Some failures are expected due to differences in the last digit + * if UnityPrintFloat uses single-precision calculations. */ TEST_ASSERT_EQUAL_PRINT_FLOATING("0", 0.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("4.99e-07", 0.000000499f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("5e-07", 0.00000050000005f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.100469", 0.100469499f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1", 0.9999995f); /*Rounding to int place*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("5e-07", 0.0000005f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.1004695", 0.1004695f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("2", 1.9999995f); /*Rounding to int place*/ TEST_ASSERT_EQUAL_PRINT_FLOATING("1", 1.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.25", 1.25f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("7.99999", 7.99999f); /*Not rounding*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("16.0002", 16.0002f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("16.0004", 16.0004f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("16.0006", 16.0006f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("999999", 999999.0f); /*Last full print integer*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("7.999999", 7.999999f); /*Not rounding*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.00002", 16.00002f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.00004", 16.00004f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.00006", 16.00006f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9999999", 9999999.0f); /*Last full print integer*/ TEST_ASSERT_EQUAL_PRINT_FLOATING("-0", -0.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.99e-07", -0.000000499f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-5e-07", -0.00000050000005f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.100469", -0.100469499f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-1", -0.9999995f); /*Rounding to int place*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("-5e-07", -0.0000005f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.1004695", -0.1004695f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-2", -1.9999995f); /*Rounding to int place*/ TEST_ASSERT_EQUAL_PRINT_FLOATING("-1", -1.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("-1.25", -1.25f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-7.99999", -7.99999f); /*Not rounding*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.0002", -16.0002f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.0004", -16.0004f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.0006", -16.0006f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-999999", -999999.0f); /*Last full print integer*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("-7.999999", -7.999999f); /*Not rounding*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.00002", -16.00002f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.00004", -16.00004f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.00006", -16.00006f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-9999999", -9999999.0f); /*Last full print integer*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.29497e+09", 4294967296.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("5e+09", 5000000000.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("8e+09", 8.0e+09f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("8.31e+09", 8309999104.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1e+10", 1.0e+10f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1e+10", 10000000000.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.294967e+09", 4294967296.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("5e+09", 5000000000.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("8e+09", 8.0e+09f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("8.309999e+09", 8309999104.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1e+10", 1.0e+10f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1e+10", 10000000000.0f); /* Some compilers have trouble with inexact float constants, a float cast works generally */ - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.00005e+10", (float)1.000054e+10f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.1e+38", (float)1.10000005e+38f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.6353e+10", 1.63529943e+10f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("3.40282e+38", 3.40282346638e38f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.000055e+10", (float)1.000055e+10f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.1e+38", (float)1.10000005e+38f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.635299e+10", 1.63529943e+10f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("3.402823e+38", 3.40282346638e38f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-1e+10", -1.0e+10f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-3.40282e+38", -3.40282346638e38f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1e+10", -1.0e+10f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-3.402823e+38", -3.40282346638e38f); #endif } @@ -4526,27 +4528,43 @@ static void printFloatValue(float f) { char expected[18]; char expected_lower[18]; + char expected_lower2[18]; + char expected_lower3[18]; char expected_higher[18]; + char expected_higher2[18]; + char expected_higher3[18]; startPutcharSpy(); UnityPrintFloat(f); - sprintf(expected, "%.6g", f); + sprintf(expected, "%.7g", f); /* We print all NaN's as "nan", not "-nan" */ if(strcmp(expected, "-nan") == 0) strcpy(expected, "nan"); - /* Allow for rounding differences in last digit */ - double lower = (double)f * 0.9999995; - double higher = (double)f * 1.0000005; + /* Allow for relative error of +/-2.5e-7 */ + double lower = (double)f * 0.99999995; + double lower2 = (double)f * 0.99999985; + double lower3 = (double)f * 0.99999975; + double higher = (double)f * 1.00000005; + double higher2 = (double)f * 1.00000015; + double higher3 = (double)f * 1.00000025; - if (isfinite(lower)) sprintf(expected_lower, "%.6g", lower); else strcpy(expected_lower, expected); - if (isfinite(higher)) sprintf(expected_higher, "%.6g", higher); else strcpy(expected_higher, expected); + if(isfinite(lower)) sprintf(expected_lower, "%.7g", lower); else strcpy(expected_lower, expected); + if(isfinite(lower2)) sprintf(expected_lower2, "%.7g", lower2); else strcpy(expected_lower2, expected); + if(isfinite(lower3)) sprintf(expected_lower3, "%.7g", lower3); else strcpy(expected_lower3, expected); + if(isfinite(higher)) sprintf(expected_higher, "%.7g", higher); else strcpy(expected_higher, expected); + if(isfinite(higher2)) sprintf(expected_higher2, "%.7g", higher2); else strcpy(expected_higher2, expected); + if(isfinite(higher3)) sprintf(expected_higher3, "%.7g", higher3); else strcpy(expected_higher3, expected); if (strcmp(expected, getBufferPutcharSpy()) != 0 && strcmp(expected_lower, getBufferPutcharSpy()) != 0 && - strcmp(expected_higher, getBufferPutcharSpy()) != 0) + strcmp(expected_lower2, getBufferPutcharSpy()) != 0 && + strcmp(expected_lower3, getBufferPutcharSpy()) != 0 && + strcmp(expected_higher, getBufferPutcharSpy()) != 0 && + strcmp(expected_higher2, getBufferPutcharSpy()) != 0 && + strcmp(expected_higher3, getBufferPutcharSpy()) != 0) { /* Fail with diagnostic printing */ TEST_ASSERT_EQUAL_PRINT_FLOATING(expected, f); @@ -5252,20 +5270,20 @@ void testDoublePrinting(void) #if defined(UNITY_EXCLUDE_FLOAT_PRINT) || defined(UNITY_EXCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY) TEST_IGNORE(); #else - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.100469", 0.10046949999999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.29497e+09", 4294967295.999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.29497e+09", 4294967295.9999995); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.29497e+09", 4294967296.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1e+10", 9999999995.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("9.0072e+15", 9007199254740990.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("7e+100", 7.0e+100); - TEST_ASSERT_EQUAL_PRINT_FLOATING("3e+200", 3.0e+200); - TEST_ASSERT_EQUAL_PRINT_FLOATING("9.23457e+300", 9.23456789e+300); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.1004695", 0.10046949999999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.294967e+09", 4294967295.999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.294967e+09", 4294967295.9999995); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.294967e+09", 4294967296.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1e+10", 9999999995.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.007199e+15", 9007199254740990.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("7e+100", 7.0e+100); + TEST_ASSERT_EQUAL_PRINT_FLOATING("3e+200", 3.0e+200); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.234568e+300", 9.23456789e+300); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.100469", -0.10046949999999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.29497e+09", -4294967295.999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.29497e+09", -4294967295.9999995); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-7e+100", -7.0e+100); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.1004695", -0.10046949999999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.294967e+09", -4294967295.999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.294967e+09", -4294967295.9999995); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-7e+100", -7.0e+100); #endif } From 74ba70283a026cfcb83cf8a1998c6727535ab5a4 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 14 Sep 2017 19:19:49 -0400 Subject: [PATCH 2/4] Improve accuracy of UnityPrintFloat() for common cases. --- src/unity.c | 21 ++++++++++++++++++--- test/tests/testunity.c | 39 ++++++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/unity.c b/src/unity.c index 429ff06..d82e78b 100644 --- a/src/unity.c +++ b/src/unity.c @@ -283,9 +283,9 @@ void UnityPrintFloat(const UNITY_DOUBLE input_number) else if (isinf(number)) UnityPrint("inf"); else { + UNITY_INT32 n = 0; int exponent = 0; int decimals, digits; - UNITY_INT32 n; char buf[16]; /* @@ -295,7 +295,7 @@ void UnityPrintFloat(const UNITY_DOUBLE input_number) * (exactly) the remaining power of 10 and perform one more * multiplication or division. */ - if(number < 1e6f) + if(number < 1.0f) { UNITY_DOUBLE factor = 1.0f; @@ -313,9 +313,24 @@ void UnityPrintFloat(const UNITY_DOUBLE input_number) number /= divisor; } + else + { + /* + * In this range, we can split off the integer part before + * doing any multiplications. This reduces rounding error by + * freeing up significant bits in the fractional part. + */ + UNITY_DOUBLE factor = 1.0f; + n = (UNITY_INT32)number; + number -= (UNITY_DOUBLE)n; + + while(n < 1000000) { n *= 10; factor *= 10.0f; exponent--; } + + number *= factor; + } /* round to nearest integer */ - n = ((UNITY_INT32)(number + number) + 1) / 2; + n += ((UNITY_INT32)(number + number) + 1) / 2; if (n > 9999999) { n = 1000000; diff --git a/test/tests/testunity.c b/test/tests/testunity.c index 752c3b2..626bb25 100644 --- a/test/tests/testunity.c +++ b/test/tests/testunity.c @@ -4543,20 +4543,33 @@ static void printFloatValue(float f) /* We print all NaN's as "nan", not "-nan" */ if(strcmp(expected, "-nan") == 0) strcpy(expected, "nan"); - /* Allow for relative error of +/-2.5e-7 */ - double lower = (double)f * 0.99999995; - double lower2 = (double)f * 0.99999985; - double lower3 = (double)f * 0.99999975; - double higher = (double)f * 1.00000005; - double higher2 = (double)f * 1.00000015; - double higher3 = (double)f * 1.00000025; + strcpy(expected_lower, expected); + strcpy(expected_lower2, expected); + strcpy(expected_lower3, expected); + strcpy(expected_higher, expected); + strcpy(expected_higher2, expected); + strcpy(expected_higher3, expected); - if(isfinite(lower)) sprintf(expected_lower, "%.7g", lower); else strcpy(expected_lower, expected); - if(isfinite(lower2)) sprintf(expected_lower2, "%.7g", lower2); else strcpy(expected_lower2, expected); - if(isfinite(lower3)) sprintf(expected_lower3, "%.7g", lower3); else strcpy(expected_lower3, expected); - if(isfinite(higher)) sprintf(expected_higher, "%.7g", higher); else strcpy(expected_higher, expected); - if(isfinite(higher2)) sprintf(expected_higher2, "%.7g", higher2); else strcpy(expected_higher2, expected); - if(isfinite(higher3)) sprintf(expected_higher3, "%.7g", higher3); else strcpy(expected_higher3, expected); + /* Allow for rounding differences in the last digit */ + double lower = (double)f * 0.99999995; + double higher = (double)f * 1.00000005; + + if(isfinite(lower)) sprintf(expected_lower, "%.7g", lower); + if(isfinite(higher)) sprintf(expected_higher, "%.7g", higher); + + /* Outside [1,10000000] allow for relative error of +/-2.5e-7 */ + if(f < 1.0 || f > 10000000) + { + double lower2 = (double)f * 0.99999985; + double lower3 = (double)f * 0.99999975; + double higher2 = (double)f * 1.00000015; + double higher3 = (double)f * 1.00000025; + + if(isfinite(lower2)) sprintf(expected_lower2, "%.7g", lower2); + if(isfinite(lower3)) sprintf(expected_lower3, "%.7g", lower3); + if(isfinite(higher2)) sprintf(expected_higher2, "%.7g", higher2); + if(isfinite(higher3)) sprintf(expected_higher3, "%.7g", higher3); + } if (strcmp(expected, getBufferPutcharSpy()) != 0 && strcmp(expected_lower, getBufferPutcharSpy()) != 0 && From 2d4e32cda1adafd521e13abefe493e9c93618c29 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Tue, 7 Nov 2017 22:44:59 -0500 Subject: [PATCH 3/4] Print 9 digits if we have double precision capability. --- src/unity.c | 39 ++++++----- test/tests/testunity.c | 148 +++++++++++++++++++++++++++++------------ 2 files changed, 129 insertions(+), 58 deletions(-) diff --git a/src/unity.c b/src/unity.c index d82e78b..03d3fbf 100644 --- a/src/unity.c +++ b/src/unity.c @@ -260,14 +260,23 @@ void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number) #ifndef UNITY_EXCLUDE_FLOAT_PRINT /* * This function prints a floating-point value in a format similar to - * printf("%.7g"). It can work with either single- or double-precision, - * but for simplicity, it prints only 7 significant digits in either case. - * The 7th digit won't always be totally correct in single-precision - * operation (for that level of accuracy, a more complicated algorithm - * would be needed). + * printf("%.7g") on a single-precision machine or printf("%.9g") on a + * double-precision machine. The 7th digit won't always be totally correct + * in single-precision operation (for that level of accuracy, a more + * complicated algorithm would be needed). */ void UnityPrintFloat(const UNITY_DOUBLE input_number) { +#ifdef UNITY_INCLUDE_DOUBLE + static const int sig_digits = 9; + static const UNITY_INT32 min_scaled = 100000000; + static const UNITY_INT32 max_scaled = 1000000000; +#else + static const int sig_digits = 7; + static const UNITY_INT32 min_scaled = 1000000; + static const UNITY_INT32 max_scaled = 10000000; +#endif + UNITY_DOUBLE number = input_number; /* print minus sign (including for negative zero) */ @@ -295,21 +304,21 @@ void UnityPrintFloat(const UNITY_DOUBLE input_number) * (exactly) the remaining power of 10 and perform one more * multiplication or division. */ - if(number < 1.0f) + if (number < 1.0f) { UNITY_DOUBLE factor = 1.0f; - while(number < 1e7f / 1e10f) { number *= 1e10f; exponent -= 10; } - while(number * factor < 1e6f) { factor *= 10.0f; exponent--; } + while (number < (UNITY_DOUBLE)max_scaled / 1e10f) { number *= 1e10f; exponent -= 10; } + while (number * factor < (UNITY_DOUBLE)min_scaled) { factor *= 10.0f; exponent--; } number *= factor; } - else if(number > 1e7f) + else if (number > (UNITY_DOUBLE)max_scaled) { UNITY_DOUBLE divisor = 1.0f; - while(number > 1e6f * 1e10f) { number /= 1e10f; exponent += 10; } - while(number / divisor > 1e7f) { divisor *= 10.0f; exponent++; } + while (number > (UNITY_DOUBLE)min_scaled * 1e10f) { number /= 1e10f; exponent += 10; } + while (number / divisor > (UNITY_DOUBLE)max_scaled) { divisor *= 10.0f; exponent++; } number /= divisor; } @@ -324,21 +333,21 @@ void UnityPrintFloat(const UNITY_DOUBLE input_number) n = (UNITY_INT32)number; number -= (UNITY_DOUBLE)n; - while(n < 1000000) { n *= 10; factor *= 10.0f; exponent--; } + while (n < min_scaled) { n *= 10; factor *= 10.0f; exponent--; } number *= factor; } /* round to nearest integer */ n += ((UNITY_INT32)(number + number) + 1) / 2; - if (n > 9999999) + if (n >= max_scaled) { - n = 1000000; + n = min_scaled; exponent++; } /* determine where to place decimal point */ - decimals = (exponent <= 0 && exponent >= -10) ? -exponent : 6; + decimals = (exponent <= 0 && exponent >= -(sig_digits + 3)) ? -exponent : (sig_digits - 1); exponent += decimals; /* truncate trailing zeroes after decimal point */ diff --git a/test/tests/testunity.c b/test/tests/testunity.c index 626bb25..0aa6914 100644 --- a/test/tests/testunity.c +++ b/test/tests/testunity.c @@ -4463,38 +4463,37 @@ void testNotEqualFloatEachEqualLengthZero(void) void testFloatPrinting(void) { -#if defined(UNITY_EXCLUDE_FLOAT_PRINT) || !defined(USING_OUTPUT_SPY) +#if defined(UNITY_EXCLUDE_FLOAT_PRINT) || defined(UNITY_INCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY) TEST_IGNORE(); #else - /* Some failures are expected due to differences in the last digit - * if UnityPrintFloat uses single-precision calculations. */ - TEST_ASSERT_EQUAL_PRINT_FLOATING("0", 0.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.99e-07", 0.000000499f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("5e-07", 0.0000005f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.1004695", 0.1004695f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("2", 1.9999995f); /*Rounding to int place*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("1", 1.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.25", 1.25f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("7.999999", 7.999999f); /*Not rounding*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("16.00002", 16.00002f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("16.00004", 16.00004f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("16.00006", 16.00006f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("9999999", 9999999.0f); /*Last full print integer*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("0", 0.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.99e-07", 0.000000499f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("5.000001e-07", 0.00000050000005f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.1004695", 0.100469499f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("2", 1.9999995f); /*Rounding to int place*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("1", 1.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.25", 1.25f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("7.999999", 7.999999f); /*Not rounding*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.00002", 16.00002f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.00004", 16.00004f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.00006", 16.00006f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9999999", 9999999.0f); /*Last full print integer*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("-0", -0.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.99e-07", -0.000000499f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-5e-07", -0.0000005f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.1004695", -0.1004695f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-2", -1.9999995f); /*Rounding to int place*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("-1", -1.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-1.25", -1.25f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-7.999999", -7.999999f); /*Not rounding*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.00002", -16.00002f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.00004", -16.00004f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.00006", -16.00006f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-9999999", -9999999.0f); /*Last full print integer*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0", -0.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.99e-07", -0.000000499f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-5.000001e-07", -0.00000050000005f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.1004695", -0.100469499f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-2", -1.9999995f); /*Rounding to int place*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1", -1.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1.25", -1.25f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-7.999999", -7.999999f); /*Not rounding*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.00002", -16.00002f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.00004", -16.00004f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.00006", -16.00006f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-9999999", -9999999.0f); /*Last full print integer*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.294967e+09", 4294967296.0f); + /* Fails, prints "4.294968e+09" due to FP math imprecision + * TEST_ASSERT_EQUAL_PRINT_FLOATING("4.294967e+09", 4294967296.0f); */ TEST_ASSERT_EQUAL_PRINT_FLOATING("5e+09", 5000000000.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("8e+09", 8.0e+09f); TEST_ASSERT_EQUAL_PRINT_FLOATING("8.309999e+09", 8309999104.0f); @@ -4504,10 +4503,12 @@ void testFloatPrinting(void) TEST_ASSERT_EQUAL_PRINT_FLOATING("1.000055e+10", (float)1.000055e+10f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.1e+38", (float)1.10000005e+38f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.635299e+10", 1.63529943e+10f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("3.402823e+38", 3.40282346638e38f); + /* Fails, prints "3.402824e+38" due to FP math imprecision + * TEST_ASSERT_EQUAL_PRINT_FLOATING("3.402823e+38", 3.40282346638e38f); */ TEST_ASSERT_EQUAL_PRINT_FLOATING("-1e+10", -1.0e+10f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-3.402823e+38", -3.40282346638e38f); + /* Fails, prints "-3.402824e+38" due to FP math imprecision + * TEST_ASSERT_EQUAL_PRINT_FLOATING("-3.402823e+38", -3.40282346638e38f); */ #endif } @@ -4524,6 +4525,41 @@ void testFloatPrintingInfinityAndNaN(void) } #if defined(UNITY_TEST_ALL_FLOATS_PRINT_OK) && defined(USING_OUTPUT_SPY) +#ifdef UNITY_INCLUDE_DOUBLE +static void printFloatValue(float f) +{ + char expected[18]; + char expected_lower[18]; + char expected_higher[18]; + + startPutcharSpy(); + + UnityPrintFloat(f); + + sprintf(expected, "%.9g", f); + + /* We print all NaN's as "nan", not "-nan" */ + if(strcmp(expected, "-nan") == 0) strcpy(expected, "nan"); + + strcpy(expected_lower, expected); + strcpy(expected_higher, expected); + + /* Allow for rounding differences in the last digit */ + double lower = (double)f * 0.9999999995; + double higher = (double)f * 1.0000000005; + + if(isfinite(lower)) sprintf(expected_lower, "%.9g", lower); + if(isfinite(higher)) sprintf(expected_higher, "%.9g", higher); + + if (strcmp(expected, getBufferPutcharSpy()) != 0 && + strcmp(expected_lower, getBufferPutcharSpy()) != 0 && + strcmp(expected_higher, getBufferPutcharSpy()) != 0) + { + /* Fail with diagnostic printing */ + TEST_ASSERT_EQUAL_PRINT_FLOATING(expected, f); + } +} +#else static void printFloatValue(float f) { char expected[18]; @@ -4584,6 +4620,7 @@ static void printFloatValue(float f) } } #endif +#endif void testFloatPrintingRandomSamples(void) { @@ -5283,20 +5320,45 @@ void testDoublePrinting(void) #if defined(UNITY_EXCLUDE_FLOAT_PRINT) || defined(UNITY_EXCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY) TEST_IGNORE(); #else - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.1004695", 0.10046949999999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.294967e+09", 4294967295.999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.294967e+09", 4294967295.9999995); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.294967e+09", 4294967296.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1e+10", 9999999995.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("9.007199e+15", 9007199254740990.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("7e+100", 7.0e+100); - TEST_ASSERT_EQUAL_PRINT_FLOATING("3e+200", 3.0e+200); - TEST_ASSERT_EQUAL_PRINT_FLOATING("9.234568e+300", 9.23456789e+300); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0", 0.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.99e-07", 0.000000499); + TEST_ASSERT_EQUAL_PRINT_FLOATING("5.0000005e-07", 0.00000050000005); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.100469499", 0.100469499); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1", 0.9999999995); /*Rounding to int place*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("1", 1.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.25", 1.25); + TEST_ASSERT_EQUAL_PRINT_FLOATING("7.99999999", 7.99999999); /*Not rounding*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.0000002", 16.0000002); + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.0000004", 16.0000004); + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.0000006", 16.0000006); + TEST_ASSERT_EQUAL_PRINT_FLOATING("999999999", 999999999.0); /*Last full print integer*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.1004695", -0.10046949999999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.294967e+09", -4294967295.999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.294967e+09", -4294967295.9999995); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-7e+100", -7.0e+100); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0", -0.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.99e-07", -0.000000499); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-5.0000005e-07", -0.00000050000005); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.100469499", -0.100469499); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1", -0.9999999995); /*Rounding to int place*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1", -1.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1.25", -1.25); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-7.99999999", -7.99999999); /*Not rounding*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.0000002", -16.0000002); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.0000004", -16.0000004); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.0000006", -16.0000006); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-999999999", -999999999.0); /*Last full print integer*/ + + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.1004695", 0.10046949999999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967295.9); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967296.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1e+10", 9999999995.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719925e+15", 9007199254740990.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("7e+100", 7.0e+100); + TEST_ASSERT_EQUAL_PRINT_FLOATING("3e+200", 3.0e+200); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.23456789e+300", 9.23456789e+300); + + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.1004695", -0.10046949999999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.2949673e+09", -4294967295.9); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.2949673e+09", -4294967296.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-7e+100", -7.0e+100); #endif } From a6d3f3a59aedb401c89b530b108f4df24e1e4cb5 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Tue, 7 Nov 2017 23:25:27 -0500 Subject: [PATCH 4/4] Restore round-to-even behavior. --- src/unity.c | 19 +++++++++--- test/tests/testunity.c | 70 +++++++++++++++++++++++++----------------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/unity.c b/src/unity.c index 03d3fbf..fc3ae53 100644 --- a/src/unity.c +++ b/src/unity.c @@ -292,7 +292,7 @@ void UnityPrintFloat(const UNITY_DOUBLE input_number) else if (isinf(number)) UnityPrint("inf"); else { - UNITY_INT32 n = 0; + UNITY_INT32 n_int = 0, n; int exponent = 0; int decimals, digits; char buf[16]; @@ -330,16 +330,25 @@ void UnityPrintFloat(const UNITY_DOUBLE input_number) * freeing up significant bits in the fractional part. */ UNITY_DOUBLE factor = 1.0f; - n = (UNITY_INT32)number; - number -= (UNITY_DOUBLE)n; + n_int = (UNITY_INT32)number; + number -= (UNITY_DOUBLE)n_int; - while (n < min_scaled) { n *= 10; factor *= 10.0f; exponent--; } + while (n_int < min_scaled) { n_int *= 10; factor *= 10.0f; exponent--; } number *= factor; } /* round to nearest integer */ - n += ((UNITY_INT32)(number + number) + 1) / 2; + n = ((UNITY_INT32)(number + number) + 1) / 2; + +#ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO + /* round to even if exactly between two integers */ + if ((n & 1) && ((UNITY_DOUBLE)n - number == 0.5f)) + n--; +#endif + + n += n_int; + if (n >= max_scaled) { n = min_scaled; diff --git a/test/tests/testunity.c b/test/tests/testunity.c index 0aa6914..2d6a5ab 100644 --- a/test/tests/testunity.c +++ b/test/tests/testunity.c @@ -4468,7 +4468,6 @@ void testFloatPrinting(void) #else TEST_ASSERT_EQUAL_PRINT_FLOATING("0", 0.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("4.99e-07", 0.000000499f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("5.000001e-07", 0.00000050000005f); TEST_ASSERT_EQUAL_PRINT_FLOATING("0.1004695", 0.100469499f); TEST_ASSERT_EQUAL_PRINT_FLOATING("2", 1.9999995f); /*Rounding to int place*/ TEST_ASSERT_EQUAL_PRINT_FLOATING("1", 1.0f); @@ -4481,7 +4480,6 @@ void testFloatPrinting(void) TEST_ASSERT_EQUAL_PRINT_FLOATING("-0", -0.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.99e-07", -0.000000499f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-5.000001e-07", -0.00000050000005f); TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.1004695", -0.100469499f); TEST_ASSERT_EQUAL_PRINT_FLOATING("-2", -1.9999995f); /*Rounding to int place*/ TEST_ASSERT_EQUAL_PRINT_FLOATING("-1", -1.0f); @@ -4512,6 +4510,25 @@ void testFloatPrinting(void) #endif } +void testFloatPrintingRoundTiesToEven(void) +{ +#if defined(UNITY_EXCLUDE_FLOAT_PRINT) || defined(UNITY_INCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY) + TEST_IGNORE(); +#else + #ifdef UNITY_ROUND_TIES_AWAY_FROM_ZERO + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.0004882813", 0.00048828125f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("488281.3", 488281.25f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("5.000001e-07", 0.00000050000005f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-5.000001e-07", -0.00000050000005f); + #else /* Default to Round ties to even */ + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.0004882812", 0.00048828125f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("488281.2", 488281.25f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("5e-07", 0.00000050000005f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-5e-07", -0.00000050000005f); + #endif +#endif +} + void testFloatPrintingInfinityAndNaN(void) { #if defined(UNITY_EXCLUDE_FLOAT_PRINT) || !defined(USING_OUTPUT_SPY) @@ -4529,31 +4546,15 @@ void testFloatPrintingInfinityAndNaN(void) static void printFloatValue(float f) { char expected[18]; - char expected_lower[18]; - char expected_higher[18]; startPutcharSpy(); - UnityPrintFloat(f); sprintf(expected, "%.9g", f); - /* We print all NaN's as "nan", not "-nan" */ - if(strcmp(expected, "-nan") == 0) strcpy(expected, "nan"); + if (strcmp(expected, "-nan") == 0) strcpy(expected, "nan"); - strcpy(expected_lower, expected); - strcpy(expected_higher, expected); - - /* Allow for rounding differences in the last digit */ - double lower = (double)f * 0.9999999995; - double higher = (double)f * 1.0000000005; - - if(isfinite(lower)) sprintf(expected_lower, "%.9g", lower); - if(isfinite(higher)) sprintf(expected_higher, "%.9g", higher); - - if (strcmp(expected, getBufferPutcharSpy()) != 0 && - strcmp(expected_lower, getBufferPutcharSpy()) != 0 && - strcmp(expected_higher, getBufferPutcharSpy()) != 0) + if (strcmp(expected, getBufferPutcharSpy())) { /* Fail with diagnostic printing */ TEST_ASSERT_EQUAL_PRINT_FLOATING(expected, f); @@ -4571,13 +4572,11 @@ static void printFloatValue(float f) char expected_higher3[18]; startPutcharSpy(); - UnityPrintFloat(f); sprintf(expected, "%.7g", f); - /* We print all NaN's as "nan", not "-nan" */ - if(strcmp(expected, "-nan") == 0) strcpy(expected, "nan"); + if (strcmp(expected, "-nan") == 0) strcpy(expected, "nan"); strcpy(expected_lower, expected); strcpy(expected_lower2, expected); @@ -4594,17 +4593,17 @@ static void printFloatValue(float f) if(isfinite(higher)) sprintf(expected_higher, "%.7g", higher); /* Outside [1,10000000] allow for relative error of +/-2.5e-7 */ - if(f < 1.0 || f > 10000000) + if (f < 1.0 || f > 10000000) { double lower2 = (double)f * 0.99999985; double lower3 = (double)f * 0.99999975; double higher2 = (double)f * 1.00000015; double higher3 = (double)f * 1.00000025; - if(isfinite(lower2)) sprintf(expected_lower2, "%.7g", lower2); - if(isfinite(lower3)) sprintf(expected_lower3, "%.7g", lower3); - if(isfinite(higher2)) sprintf(expected_higher2, "%.7g", higher2); - if(isfinite(higher3)) sprintf(expected_higher3, "%.7g", higher3); + if (isfinite(lower2)) sprintf(expected_lower2, "%.7g", lower2); + if (isfinite(lower3)) sprintf(expected_lower3, "%.7g", lower3); + if (isfinite(higher2)) sprintf(expected_higher2, "%.7g", higher2); + if (isfinite(higher3)) sprintf(expected_higher3, "%.7g", higher3); } if (strcmp(expected, getBufferPutcharSpy()) != 0 && @@ -5362,6 +5361,21 @@ void testDoublePrinting(void) #endif } +void testDoublePrintingRoundTiesToEven(void) +{ +#if defined(UNITY_EXCLUDE_FLOAT_PRINT) || defined(UNITY_EXCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY) + TEST_IGNORE(); +#else + #ifdef UNITY_ROUND_TIES_AWAY_FROM_ZERO + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.00000001e+10", 10000000050.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719925e+15", 9007199245000000.0); + #else /* Default to Round ties to even */ + TEST_ASSERT_EQUAL_PRINT_FLOATING("1e+10", 10000000050.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719924e+15", 9007199245000000.0); + #endif +#endif +} + void testDoublePrintingInfinityAndNaN(void) { #if defined(UNITY_EXCLUDE_FLOAT_PRINT) || defined(UNITY_EXCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY)