From 6ec7c78b668d06a741797440dd0a5916573e8559 Mon Sep 17 00:00:00 2001 From: jsalling Date: Sun, 4 Sep 2016 20:48:29 -0500 Subject: [PATCH 01/14] Writing a float printing routine --- src/unity.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/src/unity.c b/src/unity.c index 5740657..068d015 100644 --- a/src/unity.c +++ b/src/unity.c @@ -263,9 +263,59 @@ void UnityPrintMask(const _U_UINT mask, const _U_UINT number) void UnityPrintFloat(_UD number) { - char TempBuffer[UNITY_VERBOSE_NUMBER_MAX_LENGTH + 1]; - snprintf(TempBuffer, sizeof(TempBuffer), "%.6f", number); - UnityPrint(TempBuffer); + // char TempBuffer[UNITY_VERBOSE_NUMBER_MAX_LENGTH + 1]; + // snprintf(TempBuffer, sizeof(TempBuffer), "%.6f", number); + // UnityPrint(TempBuffer); + if (isnan(number)) + { + UnityPrint(UnityStrNaN); + return; + } + + if (number < 0) + { + UNITY_OUTPUT_CHAR('-'); + number = -number; + } + + if (isinf(number)) UnityPrintLen(UnityStrInf, 3); + else + { + _UD divisor = 1; + _U_UINT exponent = 0; + while (number / divisor >= 10.0f) + { + divisor *= 10; + exponent++; + } + /* 10000000 < 2^24, max integer cast to a float without truncation */ + #define FLOAT_SCI_FORMAT_MINIMUM 10000000 + if (number >= FLOAT_SCI_FORMAT_MINIMUM) + { /* Print in scientific format: 1.123456e38 */ + int i; + for (i = 0; i < 7; i++) + { + UNITY_OUTPUT_CHAR('0' + (int)(number / divisor) % 10); + if (i == 0) UNITY_OUTPUT_CHAR('.'); + divisor /= 10.0f; + } + UNITY_OUTPUT_CHAR('e'); + UnityPrintNumberUnsigned(exponent); + } + else + { /* Print up to 9 characters, 6 after the decimal max */ + _UD decimals = FLOAT_SCI_FORMAT_MINIMUM/divisor; + if (decimals > 1000000) decimals = 1000000; + number = number + 0.5f/decimals; /* Rounding */ + while (decimals >= 1) + { + UNITY_OUTPUT_CHAR('0' + (int)(number / divisor) % 10); + if (divisor == 1.0f) UNITY_OUTPUT_CHAR('.'); + if (divisor <= 1.0f) decimals /= 10; + divisor /= 10; + } + } + } } #endif From d4a35f0949839fecc1ef7b890906495d5cedbf75 Mon Sep 17 00:00:00 2001 From: jsalling Date: Sun, 4 Sep 2016 21:18:25 -0500 Subject: [PATCH 02/14] Refactor to delete smaller number decimal format Generalize loop to print decimal format and exponential Add '+' to exponent when printing larger floats --- src/unity.c | 49 ++++++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/src/unity.c b/src/unity.c index 068d015..e1294c3 100644 --- a/src/unity.c +++ b/src/unity.c @@ -277,47 +277,38 @@ void UnityPrintFloat(_UD number) UNITY_OUTPUT_CHAR('-'); number = -number; } - if (isinf(number)) UnityPrintLen(UnityStrInf, 3); - else + else /* Small numbers as "%.6f" format string, but uses scientific notation for larger numbers */ { - _UD divisor = 1; + _UD divisor = 1.0f; _U_UINT exponent = 0; + int scifmt; + _U_UINT i; + while (number / divisor >= 10.0f) { divisor *= 10; exponent++; } - /* 10000000 < 2^24, max integer cast to a float without truncation */ - #define FLOAT_SCI_FORMAT_MINIMUM 10000000 - if (number >= FLOAT_SCI_FORMAT_MINIMUM) - { /* Print in scientific format: 1.123456e38 */ - int i; - for (i = 0; i < 7; i++) - { - UNITY_OUTPUT_CHAR('0' + (int)(number / divisor) % 10); - if (i == 0) UNITY_OUTPUT_CHAR('.'); - divisor /= 10.0f; - } - UNITY_OUTPUT_CHAR('e'); - UnityPrintNumberUnsigned(exponent); + + number = number + 0.5f/(1000000/divisor); /* Rounding to 6 figures */ + /* Print in scientific format: 1.123456e38 */ + scifmt = exponent > 3; + for (i = 0; i < 7; i++) /* Always print 7 digits total */ + { + UNITY_OUTPUT_CHAR('0' + (int)(number / divisor) % 10); + if ((scifmt && i == 0) || (!scifmt && i == exponent)) UNITY_OUTPUT_CHAR('.'); + divisor /= 10.0f; } - else - { /* Print up to 9 characters, 6 after the decimal max */ - _UD decimals = FLOAT_SCI_FORMAT_MINIMUM/divisor; - if (decimals > 1000000) decimals = 1000000; - number = number + 0.5f/decimals; /* Rounding */ - while (decimals >= 1) - { - UNITY_OUTPUT_CHAR('0' + (int)(number / divisor) % 10); - if (divisor == 1.0f) UNITY_OUTPUT_CHAR('.'); - if (divisor <= 1.0f) decimals /= 10; - divisor /= 10; - } + if (scifmt) + { + UNITY_OUTPUT_CHAR('e'); + UNITY_OUTPUT_CHAR('+'); + UnityPrintNumberUnsigned(exponent); } } } -#endif +#endif /* UNITY_FLOAT_VERBOSE */ /*-----------------------------------------------*/ From 9653fbf7ac1321573de1d6c0de41c4c423fbd5ff Mon Sep 17 00:00:00 2001 From: jsalling Date: Sat, 24 Sep 2016 14:16:26 -0500 Subject: [PATCH 03/14] Use an integer cast to print floating point numbers more precisely Improve printing six decimal places, remove trailing 0's, fix the carry when numbers like 0.9999999 round up and print leading zeros in the decimal The first attempt at printing floats had precision issues where the last few digits would often be wrong. This next approach may yield a better algorithm for numbers less than 4.29 billion, those that fit in 32 bits. --- src/unity.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/unity.c b/src/unity.c index e1294c3..636881b 100644 --- a/src/unity.c +++ b/src/unity.c @@ -284,6 +284,33 @@ void UnityPrintFloat(_UD number) _U_UINT exponent = 0; int scifmt; _U_UINT i; + _UU32 integer_part; + _UD fraction_part; + // if (number <= 0xFFFFFFFF) /* Fits in an integer */ + { + integer_part = (_UU32)number; + fraction_part = number - integer_part; + } + + _U_UINT fraction_bits = (_U_UINT)(fraction_part * 1000000.0f + 0.5f); + if (fraction_bits == 1000000) + { + fraction_bits = 0; + integer_part += 1; + } + _U_UINT divisor_int = 100000; + + UnityPrintNumberUnsigned(integer_part); + UNITY_OUTPUT_CHAR('.'); + /* now mod and print, then divide divisor */ + do + { + UNITY_OUTPUT_CHAR((char)('0' + (fraction_bits / divisor_int))); + fraction_bits %= divisor_int; + if (fraction_bits == 0) break; // Truncate trailing 0's + divisor_int /= 10; + } while (divisor_int > 0); + UNITY_OUTPUT_CHAR(' '); while (number / divisor >= 10.0f) { From 30ba118c47cf04aa514f0d9c342c47de3463ef99 Mon Sep 17 00:00:00 2001 From: jsalling Date: Tue, 4 Oct 2016 23:18:08 -0500 Subject: [PATCH 04/14] Add printing for large numbers in exponential format Delete old method for printing --- src/unity.c | 55 ++++++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/src/unity.c b/src/unity.c index 636881b..0cfcbea 100644 --- a/src/unity.c +++ b/src/unity.c @@ -278,19 +278,10 @@ void UnityPrintFloat(_UD number) number = -number; } if (isinf(number)) UnityPrintLen(UnityStrInf, 3); - else /* Small numbers as "%.6f" format string, but uses scientific notation for larger numbers */ + else if (number < 4294967296.0f) /* Fits in an integer */ { - _UD divisor = 1.0f; - _U_UINT exponent = 0; - int scifmt; - _U_UINT i; - _UU32 integer_part; - _UD fraction_part; - // if (number <= 0xFFFFFFFF) /* Fits in an integer */ - { - integer_part = (_UU32)number; - fraction_part = number - integer_part; - } + _UU32 integer_part = (_UU32)number; + _UD fraction_part = number - integer_part; _U_UINT fraction_bits = (_U_UINT)(fraction_part * 1000000.0f + 0.5f); if (fraction_bits == 1000000) @@ -310,29 +301,33 @@ void UnityPrintFloat(_UD number) if (fraction_bits == 0) break; // Truncate trailing 0's divisor_int /= 10; } while (divisor_int > 0); - UNITY_OUTPUT_CHAR(' '); - while (number / divisor >= 10.0f) + + } + else /* Won't fit in an integer type */ + { + _UU32 integer_part; + _UD divide = 10.0f; + _U_UINT exponent = 7; + + while (number / divide >= 10000000.0f) { - divisor *= 10; + divide *= 10; exponent++; } + integer_part = (_UU32)(number / divide + 0.5f); + _UU32 divisor_int = 1000000; + do + { + UNITY_OUTPUT_CHAR((char)('0' + (integer_part / divisor_int))); - number = number + 0.5f/(1000000/divisor); /* Rounding to 6 figures */ - /* Print in scientific format: 1.123456e38 */ - scifmt = exponent > 3; - for (i = 0; i < 7; i++) /* Always print 7 digits total */ - { - UNITY_OUTPUT_CHAR('0' + (int)(number / divisor) % 10); - if ((scifmt && i == 0) || (!scifmt && i == exponent)) UNITY_OUTPUT_CHAR('.'); - divisor /= 10.0f; - } - if (scifmt) - { - UNITY_OUTPUT_CHAR('e'); - UNITY_OUTPUT_CHAR('+'); - UnityPrintNumberUnsigned(exponent); - } + integer_part %= divisor_int; + divisor_int /= 10; + if (divisor_int == 100000) UNITY_OUTPUT_CHAR('.'); + } while (divisor_int > 0); + UNITY_OUTPUT_CHAR('e'); + UNITY_OUTPUT_CHAR('+'); + UnityPrintNumberUnsigned(exponent); } } #endif /* UNITY_FLOAT_VERBOSE */ From e48fe0a07c087a313ed3baf2eeb47951cf3b32d1 Mon Sep 17 00:00:00 2001 From: jsalling Date: Wed, 12 Oct 2016 21:58:28 -0500 Subject: [PATCH 05/14] Reorganize NaN and Inf printing into if-else blocks --- src/unity.c | 58 +++++++++++++++++++----------------------- test/tests/testunity.c | 12 +++++++++ 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/unity.c b/src/unity.c index 0cfcbea..44c870f 100644 --- a/src/unity.c +++ b/src/unity.c @@ -251,58 +251,50 @@ void UnityPrintMask(const _U_UINT mask, const _U_UINT number) /*-----------------------------------------------*/ #ifdef UNITY_FLOAT_VERBOSE -#include - -#ifndef UNITY_VERBOSE_NUMBER_MAX_LENGTH -# ifdef UNITY_DOUBLE_VERBOSE -# define UNITY_VERBOSE_NUMBER_MAX_LENGTH 317 -# else -# define UNITY_VERBOSE_NUMBER_MAX_LENGTH 47 -# endif -#endif +/* + * char buffer[19]; + * if (number > 4294967296.0 || -number > 4294967296.0) + * snprintf(buffer, sizeof buffer, "%.6e", number); + * else + * snprintf(buffer, sizeof buffer, "%.6f", number); + * UnityPrint(buffer); + */ void UnityPrintFloat(_UD number) { - // char TempBuffer[UNITY_VERBOSE_NUMBER_MAX_LENGTH + 1]; - // snprintf(TempBuffer, sizeof(TempBuffer), "%.6f", number); - // UnityPrint(TempBuffer); - if (isnan(number)) - { - UnityPrint(UnityStrNaN); - return; - } - if (number < 0) { UNITY_OUTPUT_CHAR('-'); number = -number; } - if (isinf(number)) UnityPrintLen(UnityStrInf, 3); + + if (isnan(number)) UnityPrint(UnityStrNaN); + else if (isinf(number)) UnityPrintLen(UnityStrInf, 3); + else if (number < 0.0000005 && number > 0) UnityPrint("0.000000..."); /* Small number format */ else if (number < 4294967296.0f) /* Fits in an integer */ { _UU32 integer_part = (_UU32)number; _UD fraction_part = number - integer_part; _U_UINT fraction_bits = (_U_UINT)(fraction_part * 1000000.0f + 0.5f); + if (fraction_bits == 1000000) { fraction_bits = 0; integer_part += 1; } - _U_UINT divisor_int = 100000; + _U_UINT divisor = 100000; UnityPrintNumberUnsigned(integer_part); UNITY_OUTPUT_CHAR('.'); /* now mod and print, then divide divisor */ do { - UNITY_OUTPUT_CHAR((char)('0' + (fraction_bits / divisor_int))); - fraction_bits %= divisor_int; + UNITY_OUTPUT_CHAR((char)('0' + (fraction_bits / divisor))); + fraction_bits %= divisor; if (fraction_bits == 0) break; // Truncate trailing 0's - divisor_int /= 10; - } while (divisor_int > 0); - - + divisor /= 10; + } while (divisor > 0); } else /* Won't fit in an integer type */ { @@ -316,17 +308,19 @@ void UnityPrintFloat(_UD number) exponent++; } integer_part = (_UU32)(number / divide + 0.5f); - _UU32 divisor_int = 1000000; + + _UU32 divisor = 1000000; do { - UNITY_OUTPUT_CHAR((char)('0' + (integer_part / divisor_int))); + UNITY_OUTPUT_CHAR((char)('0' + (integer_part / divisor))); - integer_part %= divisor_int; - divisor_int /= 10; - if (divisor_int == 100000) UNITY_OUTPUT_CHAR('.'); - } while (divisor_int > 0); + integer_part %= divisor; + divisor /= 10; + if (divisor == 100000) UNITY_OUTPUT_CHAR('.'); + } while (divisor > 0); UNITY_OUTPUT_CHAR('e'); UNITY_OUTPUT_CHAR('+'); + if (exponent < 10) UNITY_OUTPUT_CHAR('0'); UnityPrintNumberUnsigned(exponent); } } diff --git a/test/tests/testunity.c b/test/tests/testunity.c index e66de4d..75c3552 100644 --- a/test/tests/testunity.c +++ b/test/tests/testunity.c @@ -3228,6 +3228,18 @@ void testNotEqualFloatArraysLengthZero(void) #endif } +void testFloatVerbosePrinting(void) +{ +#ifdef UNITY_FLOAT_VERBOSE + UnityPrintFloat(123456789.0f); + UnityPrintFloat(100000000.0f); + UnityPrintFloat(65536.0f*65536.0f); + UnityPrintFloat(1000000000.0f); + UnityPrintFloat(10000000000.0f); + UnityPrintFloat(9999999000.0f); +#endif +} + // ===================== THESE TEST WILL RUN IF YOUR CONFIG INCLUDES DOUBLE SUPPORT ================== void testDoublesWithinDelta(void) From 2de0e8285d2767c50712d035c686bdcb61c11b17 Mon Sep 17 00:00:00 2001 From: jsalling Date: Thu, 3 Nov 2016 23:56:32 -0500 Subject: [PATCH 06/14] Key idea is using double precision calculations makes everything better Print 9 digits --- src/unity.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/unity.c b/src/unity.c index 44c870f..7873385 100644 --- a/src/unity.c +++ b/src/unity.c @@ -255,7 +255,7 @@ void UnityPrintMask(const _U_UINT mask, const _U_UINT number) /* * char buffer[19]; * if (number > 4294967296.0 || -number > 4294967296.0) - * snprintf(buffer, sizeof buffer, "%.6e", number); + * snprintf(buffer, sizeof buffer, "%.8e", number); * else * snprintf(buffer, sizeof buffer, "%.6f", number); * UnityPrint(buffer); @@ -276,7 +276,8 @@ void UnityPrintFloat(_UD number) _UU32 integer_part = (_UU32)number; _UD fraction_part = number - integer_part; - _U_UINT fraction_bits = (_U_UINT)(fraction_part * 1000000.0f + 0.5f); + _U_UINT fraction_bits = (_U_UINT)(fraction_part * 1000000.0 + 0.5); + /* Double precision calculation gives best performance for six rounded decimal places */ if (fraction_bits == 1000000) { @@ -299,24 +300,25 @@ void UnityPrintFloat(_UD number) else /* Won't fit in an integer type */ { _UU32 integer_part; - _UD divide = 10.0f; - _U_UINT exponent = 7; + double divide = 10.0; + _U_UINT exponent = 9; - while (number / divide >= 10000000.0f) + while (number / divide >= 1000000000.0) { divide *= 10; exponent++; } - integer_part = (_UU32)(number / divide + 0.5f); + integer_part = (_UU32)(number / divide + 0.5); + /* Double precision calculation required for float, to produce 9 rounded digits */ - _UU32 divisor = 1000000; + _UU32 divisor = 100000000; do { UNITY_OUTPUT_CHAR((char)('0' + (integer_part / divisor))); integer_part %= divisor; divisor /= 10; - if (divisor == 100000) UNITY_OUTPUT_CHAR('.'); + if (divisor == 10000000) UNITY_OUTPUT_CHAR('.'); } while (divisor > 0); UNITY_OUTPUT_CHAR('e'); UNITY_OUTPUT_CHAR('+'); From 393f2cb5448f48ac592fdeb90cbcc8d0ef766a74 Mon Sep 17 00:00:00 2001 From: jsalling Date: Fri, 4 Nov 2016 23:56:50 -0500 Subject: [PATCH 07/14] Refactor printing after the decimal point, signed types, small numbers... Change some types to signed for simpler code and speed Added format to distinguish small numbers --- src/unity.c | 59 +++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/unity.c b/src/unity.c index 7873385..8ed1064 100644 --- a/src/unity.c +++ b/src/unity.c @@ -251,6 +251,17 @@ void UnityPrintMask(const _U_UINT mask, const _U_UINT number) /*-----------------------------------------------*/ #ifdef UNITY_FLOAT_VERBOSE +static void UnityPrintDecimalAndNumberWithLeadingZeros(_US32 fraction_part, _US32 divisor) +{ + UNITY_OUTPUT_CHAR('.'); + while (divisor > 0) + { + UNITY_OUTPUT_CHAR('0' + fraction_part / divisor); + fraction_part %= divisor; + divisor /= 10; + if (fraction_part == 0) break; /* Truncate trailing 0's */ + } +} /* * char buffer[19]; @@ -270,60 +281,46 @@ void UnityPrintFloat(_UD number) if (isnan(number)) UnityPrint(UnityStrNaN); else if (isinf(number)) UnityPrintLen(UnityStrInf, 3); - else if (number < 0.0000005 && number > 0) UnityPrint("0.000000..."); /* Small number format */ - else if (number < 4294967296.0f) /* Fits in an integer */ + else if (number < 0.0000005 && number > 0) UnityPrint("0.000000..."); /* Small numbers */ + else if (number < 4294967296.0f) /* Rounded result fits in 32 bits, "%.6f" format */ { + _US32 divisor = (1000000/10); _UU32 integer_part = (_UU32)number; - _UD fraction_part = number - integer_part; - - _U_UINT fraction_bits = (_U_UINT)(fraction_part * 1000000.0 + 0.5); + _US32 fraction_part = (_US32)((number - integer_part)*1000000.0 + 0.5); /* Double precision calculation gives best performance for six rounded decimal places */ - if (fraction_bits == 1000000) + if (fraction_part == 1000000) { - fraction_bits = 0; + fraction_part = 0; integer_part += 1; } - _U_UINT divisor = 100000; UnityPrintNumberUnsigned(integer_part); - UNITY_OUTPUT_CHAR('.'); - /* now mod and print, then divide divisor */ - do - { - UNITY_OUTPUT_CHAR((char)('0' + (fraction_bits / divisor))); - fraction_bits %= divisor; - if (fraction_bits == 0) break; // Truncate trailing 0's - divisor /= 10; - } while (divisor > 0); + UnityPrintDecimalAndNumberWithLeadingZeros(fraction_part, divisor); } - else /* Won't fit in an integer type */ + else /* Number is larger, use exponential format of 9 digits, "%.8e" */ { - _UU32 integer_part; + _US32 divisor = (1000000000/10); + _US32 integer_part; double divide = 10.0; - _U_UINT exponent = 9; + int exponent = 9; while (number / divide >= 1000000000.0) { divide *= 10; exponent++; } - integer_part = (_UU32)(number / divide + 0.5); + integer_part = (_US32)(number / divide + 0.5); /* Double precision calculation required for float, to produce 9 rounded digits */ - _UU32 divisor = 100000000; - do - { - UNITY_OUTPUT_CHAR((char)('0' + (integer_part / divisor))); - - integer_part %= divisor; - divisor /= 10; - if (divisor == 10000000) UNITY_OUTPUT_CHAR('.'); - } while (divisor > 0); + UNITY_OUTPUT_CHAR('0' + integer_part / divisor); + integer_part %= divisor; + divisor /= 10; + UnityPrintDecimalAndNumberWithLeadingZeros(integer_part, divisor); UNITY_OUTPUT_CHAR('e'); UNITY_OUTPUT_CHAR('+'); if (exponent < 10) UNITY_OUTPUT_CHAR('0'); - UnityPrintNumberUnsigned(exponent); + UnityPrintNumber(exponent); } } #endif /* UNITY_FLOAT_VERBOSE */ From 1dfcb54491f6c53087dc7e16874bb6f3efb5c550 Mon Sep 17 00:00:00 2001 From: jsalling Date: Sun, 6 Nov 2016 22:22:11 -0600 Subject: [PATCH 08/14] Start adding tests. Add const and simplify code. --- src/unity.c | 8 +++---- test/tests/testunity.c | 54 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/unity.c b/src/unity.c index 8ed1064..ecc9559 100644 --- a/src/unity.c +++ b/src/unity.c @@ -284,7 +284,7 @@ void UnityPrintFloat(_UD number) else if (number < 0.0000005 && number > 0) UnityPrint("0.000000..."); /* Small numbers */ else if (number < 4294967296.0f) /* Rounded result fits in 32 bits, "%.6f" format */ { - _US32 divisor = (1000000/10); + const _US32 divisor = (1000000/10); _UU32 integer_part = (_UU32)number; _US32 fraction_part = (_US32)((number - integer_part)*1000000.0 + 0.5); /* Double precision calculation gives best performance for six rounded decimal places */ @@ -300,7 +300,7 @@ void UnityPrintFloat(_UD number) } else /* Number is larger, use exponential format of 9 digits, "%.8e" */ { - _US32 divisor = (1000000000/10); + const _US32 divisor = (1000000000/10); _US32 integer_part; double divide = 10.0; int exponent = 9; @@ -314,9 +314,7 @@ void UnityPrintFloat(_UD number) /* Double precision calculation required for float, to produce 9 rounded digits */ UNITY_OUTPUT_CHAR('0' + integer_part / divisor); - integer_part %= divisor; - divisor /= 10; - UnityPrintDecimalAndNumberWithLeadingZeros(integer_part, divisor); + UnityPrintDecimalAndNumberWithLeadingZeros(integer_part % divisor, divisor / 10); UNITY_OUTPUT_CHAR('e'); UNITY_OUTPUT_CHAR('+'); if (exponent < 10) UNITY_OUTPUT_CHAR('0'); diff --git a/test/tests/testunity.c b/test/tests/testunity.c index 75c3552..76babfc 100644 --- a/test/tests/testunity.c +++ b/test/tests/testunity.c @@ -3228,15 +3228,57 @@ void testNotEqualFloatArraysLengthZero(void) #endif } +#ifdef UNITY_FLOAT_VERBOSE +#define TEST_ASSERT_EQUAL_PRINT_FLOATING(expected, actual) { \ + startPutcharSpy(); UnityPrintFloat((actual)); endPutcharSpy(); \ + TEST_ASSERT_EQUAL_STRING((expected), getBufferPutcharSpy()); \ + } +#endif + void testFloatVerbosePrinting(void) { #ifdef UNITY_FLOAT_VERBOSE - UnityPrintFloat(123456789.0f); - UnityPrintFloat(100000000.0f); - UnityPrintFloat(65536.0f*65536.0f); - UnityPrintFloat(1000000000.0f); - UnityPrintFloat(10000000000.0f); - UnityPrintFloat(9999999000.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.0", 0.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.000000...", 0.000000499f); + float smallest = 0.0000005f; + *(int*)&smallest += 1; + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.000001", smallest); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.100469", 0.100469499f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0", 0.9999995f); /*Rounding to int place*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0", 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.000002", 16.000002f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.000004", 16.000004f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.000006", 16.000006f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4294967040.0", 4294967040.0f); /*Last full print integer*/ + + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967296.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("5.0e+09", 5000000000.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("8.0e+09", 8.0e+09f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("8.3099991e+09", 8309999104.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 1.0e+10f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 10000000000.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.00005499e+10", 1.000055e+10f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.10000006e+38", 1.10000005e+38f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.63529943e+10", 1.63529943e+10f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("3.40282347e+38", 3.40282346638e38f); + + TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 3.40282346638e38f*2.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.0f / f_zero); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-Inf", -3.40282346638e38f*2.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", -3.40282346638e38f*2.0f * f_zero); + + //Double + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.100469", 0.10046949999999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4294967295.999999", 4294967295.999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967296.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("7.0e+100", 7.0e+100); + + TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.7976931348623157e308*10.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.0 / d_zero); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-Inf", -1.7976931348623157e308*10.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", -1.7976931348623157e308*10.0 * d_zero); #endif } From 4a27d147348e6698563e8a692cdcb7f29e641fc2 Mon Sep 17 00:00:00 2001 From: jsalling Date: Sun, 6 Nov 2016 22:25:54 -0600 Subject: [PATCH 09/14] Correct boundary conditions and add tests --- src/unity.c | 6 +++--- test/tests/testunity.c | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/unity.c b/src/unity.c index ecc9559..c282873 100644 --- a/src/unity.c +++ b/src/unity.c @@ -281,8 +281,8 @@ void UnityPrintFloat(_UD number) if (isnan(number)) UnityPrint(UnityStrNaN); else if (isinf(number)) UnityPrintLen(UnityStrInf, 3); - else if (number < 0.0000005 && number > 0) UnityPrint("0.000000..."); /* Small numbers */ - else if (number < 4294967296.0f) /* Rounded result fits in 32 bits, "%.6f" format */ + else if (number <= 0.0000005 && number > 0) UnityPrint("0.000000..."); /* Small number */ + else if (number < 4294967295.9999995) /* Rounded result fits in 32 bits, "%.6f" format */ { const _US32 divisor = (1000000/10); _UU32 integer_part = (_UU32)number; @@ -305,7 +305,7 @@ void UnityPrintFloat(_UD number) double divide = 10.0; int exponent = 9; - while (number / divide >= 1000000000.0) + while (number / divide >= 1000000000.0 - 0.5) { divide *= 10; exponent++; diff --git a/test/tests/testunity.c b/test/tests/testunity.c index 76babfc..aa07dee 100644 --- a/test/tests/testunity.c +++ b/test/tests/testunity.c @@ -3272,7 +3272,9 @@ void testFloatVerbosePrinting(void) //Double TEST_ASSERT_EQUAL_PRINT_FLOATING("0.100469", 0.10046949999999999); TEST_ASSERT_EQUAL_PRINT_FLOATING("4294967295.999999", 4294967295.999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967295.9999995); TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967296.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 9999999995.0); TEST_ASSERT_EQUAL_PRINT_FLOATING("7.0e+100", 7.0e+100); TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.7976931348623157e308*10.0); From 54fe786fae055e36082410fd0bebba7578df4c39 Mon Sep 17 00:00:00 2001 From: jsalling Date: Wed, 9 Nov 2016 23:07:31 -0600 Subject: [PATCH 10/14] Round ties to even by default, many C libraries follow this Linux gcc & clang and OSX clang produce output with ties round to even Windows mingw gcc does not Example 0.0078125 prints '0.007812' --- src/unity.c | 5 +++++ test/tests/testunity.c | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/unity.c b/src/unity.c index c282873..2cf327f 100644 --- a/src/unity.c +++ b/src/unity.c @@ -262,6 +262,9 @@ static void UnityPrintDecimalAndNumberWithLeadingZeros(_US32 fraction_part, _US3 if (fraction_part == 0) break; /* Truncate trailing 0's */ } } +#define ROUND_TIES_TO_EVEN(num_int, num) \ + if ((num_int & 1) == 1 && num_int > (num)) /* Odd and was rounded up */ \ + if ((num) - (_US32)(num) <= 0.5) num_int -= 1 /* and remainder was 0.5, a tie */ /* * char buffer[19]; @@ -288,6 +291,7 @@ void UnityPrintFloat(_UD number) _UU32 integer_part = (_UU32)number; _US32 fraction_part = (_US32)((number - integer_part)*1000000.0 + 0.5); /* Double precision calculation gives best performance for six rounded decimal places */ + ROUND_TIES_TO_EVEN(fraction_part, (number - integer_part)*1000000.0); if (fraction_part == 1000000) { @@ -312,6 +316,7 @@ void UnityPrintFloat(_UD number) } integer_part = (_US32)(number / divide + 0.5); /* Double precision calculation required for float, to produce 9 rounded digits */ + ROUND_TIES_TO_EVEN(integer_part, number / divide); UNITY_OUTPUT_CHAR('0' + integer_part / divisor); UnityPrintDecimalAndNumberWithLeadingZeros(integer_part % divisor, divisor / 10); diff --git a/test/tests/testunity.c b/test/tests/testunity.c index aa07dee..b08d7de 100644 --- a/test/tests/testunity.c +++ b/test/tests/testunity.c @@ -3243,6 +3243,8 @@ void testFloatVerbosePrinting(void) float smallest = 0.0000005f; *(int*)&smallest += 1; TEST_ASSERT_EQUAL_PRINT_FLOATING("0.000001", smallest); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.007812", 0.0078125f); /*not if ties round away from 0*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.976562", 0.9765625f); /*not if ties round away from 0*/ TEST_ASSERT_EQUAL_PRINT_FLOATING("0.100469", 0.100469499f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0", 0.9999995f); /*Rounding to int place*/ TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0", 1.0f); @@ -3259,6 +3261,7 @@ void testFloatVerbosePrinting(void) TEST_ASSERT_EQUAL_PRINT_FLOATING("8.3099991e+09", 8309999104.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 1.0e+10f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 10000000000.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.00005499e+10", 1.000055e+10f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.10000006e+38", 1.10000005e+38f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.63529943e+10", 1.63529943e+10f); @@ -3275,6 +3278,9 @@ void testFloatVerbosePrinting(void) TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967295.9999995); TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967296.0); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 9999999995.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 10000000050.0); /*not if ties round away from 0*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719924e+15", 9007199245000000.0); /*not if ties round away from 0*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719925e+15", 9007199254740990.0); TEST_ASSERT_EQUAL_PRINT_FLOATING("7.0e+100", 7.0e+100); TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.7976931348623157e308*10.0); From 47f6a85b8c502dabbeb2b5c8381d59a1efad1d89 Mon Sep 17 00:00:00 2001 From: jsalling Date: Sun, 13 Nov 2016 23:47:16 -0600 Subject: [PATCH 11/14] Make UnityPrintFloat on by default Remove UNITY_FLOAT_VERBOSE entirely, add option UNITY_EXCLUDE_FLOAT_PRINT Remove some questionable float casts from doubles Default to Round Ties to Even behavior, add option to Round Ties Away from Zero --- src/unity.c | 29 +++++++++++++++++------------ src/unity.h | 4 +--- src/unity_internals.h | 16 +++------------- test/tests/testunity.c | 8 +++++--- 4 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/unity.c b/src/unity.c index 2cf327f..0dad740 100644 --- a/src/unity.c +++ b/src/unity.c @@ -250,7 +250,7 @@ void UnityPrintMask(const _U_UINT mask, const _U_UINT number) } /*-----------------------------------------------*/ -#ifdef UNITY_FLOAT_VERBOSE +#ifndef UNITY_EXCLUDE_FLOAT_PRINT static void UnityPrintDecimalAndNumberWithLeadingZeros(_US32 fraction_part, _US32 divisor) { UNITY_OUTPUT_CHAR('.'); @@ -262,9 +262,13 @@ static void UnityPrintDecimalAndNumberWithLeadingZeros(_US32 fraction_part, _US3 if (fraction_part == 0) break; /* Truncate trailing 0's */ } } -#define ROUND_TIES_TO_EVEN(num_int, num) \ +#ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO + #define ROUND_TIES_TO_EVEN(num_int, num) \ if ((num_int & 1) == 1 && num_int > (num)) /* Odd and was rounded up */ \ if ((num) - (_US32)(num) <= 0.5) num_int -= 1 /* and remainder was 0.5, a tie */ +#else + #define ROUND_TIES_TO_EVEN(num_int, num) +#endif /* * char buffer[19]; @@ -326,7 +330,7 @@ void UnityPrintFloat(_UD number) UnityPrintNumber(exponent); } } -#endif /* UNITY_FLOAT_VERBOSE */ +#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ /*-----------------------------------------------*/ @@ -691,6 +695,7 @@ void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, #endif #ifndef UNITY_EXCLUDE_FLOAT + static int UnityFloatsWithin(_UF delta, _UF expected, _UF actual) { _UF diff; @@ -753,7 +758,7 @@ void UnityAssertFloatsWithin(const _UF delta, if (!UnityFloatsWithin(delta, expected, actual)) { UnityTestResultsFailBegin(lineNumber); -#ifdef UNITY_FLOAT_VERBOSE +#ifndef UNITY_EXCLUDE_FLOAT_PRINT UnityPrint(UnityStrExpected); UnityPrintFloat(expected); UnityPrint(UnityStrWas); @@ -818,7 +823,7 @@ void UnityAssertFloatSpecial(const _UF actual, UnityPrint(UnityStrNot); UnityPrint(trait_names[trait_index]); UnityPrint(UnityStrWas); -#ifdef UNITY_FLOAT_VERBOSE +#ifndef UNITY_EXCLUDE_FLOAT_PRINT UnityPrintFloat(actual); #else if (should_be_trait) @@ -867,11 +872,11 @@ void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const _UD* expected, UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrElement); UnityPrintNumberUnsigned(num_elements - elements - 1); -#ifdef UNITY_DOUBLE_VERBOSE +#ifndef UNITY_EXCLUDE_FLOAT_PRINT UnityPrint(UnityStrExpected); - UnityPrintFloat((float)(*ptr_expected)); + UnityPrintFloat(*ptr_expected); UnityPrint(UnityStrWas); - UnityPrintFloat((float)(*ptr_actual)); + UnityPrintFloat(*ptr_actual); #else UnityPrint(UnityStrDelta); #endif @@ -895,11 +900,11 @@ void UnityAssertDoublesWithin(const _UD delta, if (!UnityDoublesWithin(delta, expected, actual)) { UnityTestResultsFailBegin(lineNumber); -#ifdef UNITY_DOUBLE_VERBOSE +#ifndef UNITY_EXCLUDE_FLOAT_PRINT UnityPrint(UnityStrExpected); - UnityPrintFloat((float)expected); + UnityPrintFloat(expected); UnityPrint(UnityStrWas); - UnityPrintFloat((float)actual); + UnityPrintFloat(actual); #else UnityPrint(UnityStrDelta); #endif @@ -961,7 +966,7 @@ void UnityAssertDoubleSpecial(const _UD actual, UnityPrint(UnityStrNot); UnityPrint(trait_names[trait_index]); UnityPrint(UnityStrWas); -#ifdef UNITY_DOUBLE_VERBOSE +#ifndef UNITY_EXCLUDE_FLOAT_PRINT UnityPrintFloat(actual); #else if (should_be_trait) diff --git a/src/unity.h b/src/unity.h index 031ccc9..97681ad 100644 --- a/src/unity.h +++ b/src/unity.h @@ -37,13 +37,11 @@ void tearDown(void); * - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons * - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT * - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats - * - define UNITY_FLOAT_VERBOSE to print floating point values in errors (uses sprintf) * - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons * - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default) * - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE * - define UNITY_DOUBLE_TYPE to specify something other than double - * - define UNITY_DOUBLE_VERBOSE to print floating point values in errors (uses sprintf) - * - define UNITY_VERBOSE_NUMBER_MAX_LENGTH to change maximum length of printed numbers (used by sprintf) + * - define UNITY_EXCLUDE_FLOAT_PRINT to trim binary size, won't print floating point values in errors * Output * - by default, Unity prints to standard out with putchar. define UNITY_OUTPUT_CHAR(a) with a different function if desired diff --git a/src/unity_internals.h b/src/unity_internals.h index 8ccd66d..673150e 100644 --- a/src/unity_internals.h +++ b/src/unity_internals.h @@ -171,7 +171,6 @@ #undef UNITY_INCLUDE_FLOAT #undef UNITY_FLOAT_PRECISION #undef UNITY_FLOAT_TYPE -#undef UNITY_FLOAT_VERBOSE #else @@ -225,16 +224,13 @@ typedef UNITY_FLOAT_TYPE _UF; /* No Floating Point Support */ #undef UNITY_DOUBLE_PRECISION #undef UNITY_DOUBLE_TYPE - #undef UNITY_DOUBLE_VERBOSE #ifdef UNITY_INCLUDE_DOUBLE #undef UNITY_INCLUDE_DOUBLE #endif - #ifdef UNITY_FLOAT_VERBOSE - typedef _UF _UD; - /* For parameter in UnityPrintFloat, double promotion required */ - #endif + typedef _UF _UD; + /* For parameter in UnityPrintFloat(_UD), which aliases to double or float */ #else @@ -250,12 +246,6 @@ typedef UNITY_FLOAT_TYPE _UF; #endif -#ifdef UNITY_DOUBLE_VERBOSE -#ifndef UNITY_FLOAT_VERBOSE -#define UNITY_FLOAT_VERBOSE -#endif -#endif - /*------------------------------------------------------- * Output Method: stdout (DEFAULT) *-------------------------------------------------------*/ @@ -443,7 +433,7 @@ void UnityPrintNumber(const _U_SINT number); void UnityPrintNumberUnsigned(const _U_UINT number); void UnityPrintNumberHex(const _U_UINT number, const char nibbles); -#ifdef UNITY_FLOAT_VERBOSE +#ifndef UNITY_EXCLUDE_FLOAT_PRINT void UnityPrintFloat(const _UD number); #endif diff --git a/test/tests/testunity.c b/test/tests/testunity.c index b08d7de..08289da 100644 --- a/test/tests/testunity.c +++ b/test/tests/testunity.c @@ -2285,10 +2285,14 @@ void testFailureCountIncrementsAndIsReturnedAtEnd(void) void testCstringsEscapeSequence(void) { +#ifndef USING_OUTPUT_SPY + TEST_IGNORE(); +#else startPutcharSpy(); UnityPrint("\x16\x10"); endPutcharSpy(); TEST_ASSERT_EQUAL_STRING("\\x16\\x10", getBufferPutcharSpy()); +#endif } #define TEST_ASSERT_EQUAL_PRINT_NUMBERS(expected, actual) { \ @@ -3228,16 +3232,14 @@ void testNotEqualFloatArraysLengthZero(void) #endif } -#ifdef UNITY_FLOAT_VERBOSE #define TEST_ASSERT_EQUAL_PRINT_FLOATING(expected, actual) { \ startPutcharSpy(); UnityPrintFloat((actual)); endPutcharSpy(); \ TEST_ASSERT_EQUAL_STRING((expected), getBufferPutcharSpy()); \ } -#endif void testFloatVerbosePrinting(void) { -#ifdef UNITY_FLOAT_VERBOSE +#if !defined(UNITY_EXCLUDE_FLOAT_PRINT) && defined(USING_OUTPUT_SPY) TEST_ASSERT_EQUAL_PRINT_FLOATING("0.0", 0.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("0.000000...", 0.000000499f); float smallest = 0.0000005f; From 9f4b1a332f28f1369f901abb373b6dfc3db10520 Mon Sep 17 00:00:00 2001 From: jsalling Date: Mon, 14 Nov 2016 23:10:18 -0600 Subject: [PATCH 12/14] Tests for Printing Floating Point numbers --- src/unity.c | 2 +- test/tests/testunity.c | 97 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 85 insertions(+), 14 deletions(-) diff --git a/src/unity.c b/src/unity.c index 0dad740..48f0be3 100644 --- a/src/unity.c +++ b/src/unity.c @@ -297,7 +297,7 @@ void UnityPrintFloat(_UD number) /* Double precision calculation gives best performance for six rounded decimal places */ ROUND_TIES_TO_EVEN(fraction_part, (number - integer_part)*1000000.0); - if (fraction_part == 1000000) + if (fraction_part == 1000000) /* Carry across the decimal point */ { fraction_part = 0; integer_part += 1; diff --git a/test/tests/testunity.c b/test/tests/testunity.c index 08289da..9a0f714 100644 --- a/test/tests/testunity.c +++ b/test/tests/testunity.c @@ -7,6 +7,7 @@ #include "unity.h" #include #include +#include // Dividing by these constants produces +/- infinity. // The rationale is given in UnityAssertFloatIsInf's body. @@ -2237,7 +2238,7 @@ void testIgnoredAndThenFailInTearDown(void) #endif #ifdef USING_OUTPUT_SPY -#include +int putchar(int); #define SPY_BUFFER_MAX 40 static char putcharSpyBuffer[SPY_BUFFER_MAX]; #endif @@ -3237,16 +3238,14 @@ void testNotEqualFloatArraysLengthZero(void) TEST_ASSERT_EQUAL_STRING((expected), getBufferPutcharSpy()); \ } -void testFloatVerbosePrinting(void) +void testFloatPrinting(void) { -#if !defined(UNITY_EXCLUDE_FLOAT_PRINT) && defined(USING_OUTPUT_SPY) +#if defined(UNITY_EXCLUDE_FLOAT_PRINT) || !defined(USING_OUTPUT_SPY) + TEST_IGNORE(); +#else TEST_ASSERT_EQUAL_PRINT_FLOATING("0.0", 0.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("0.000000...", 0.000000499f); - float smallest = 0.0000005f; - *(int*)&smallest += 1; - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.000001", smallest); - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.007812", 0.0078125f); /*not if ties round away from 0*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.976562", 0.9765625f); /*not if ties round away from 0*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.000001", 0.00000050000005f); TEST_ASSERT_EQUAL_PRINT_FLOATING("0.100469", 0.100469499f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0", 0.9999995f); /*Rounding to int place*/ TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0", 1.0f); @@ -3257,37 +3256,109 @@ void testFloatVerbosePrinting(void) TEST_ASSERT_EQUAL_PRINT_FLOATING("16.000006", 16.000006f); TEST_ASSERT_EQUAL_PRINT_FLOATING("4294967040.0", 4294967040.0f); /*Last full print integer*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.0", -0.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.000000...",-0.000000499f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.000001", -0.00000050000005f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.100469", -0.100469499f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1.0", -0.9999995f); /*Rounding to int place*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1.0", -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.000002", -16.000002f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.000004", -16.000004f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.000006", -16.000006f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4294967040.0",-4294967040.0f); /*Last full print integer*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967296.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("5.0e+09", 5000000000.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("8.0e+09", 8.0e+09f); TEST_ASSERT_EQUAL_PRINT_FLOATING("8.3099991e+09", 8309999104.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 1.0e+10f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 10000000000.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.00005499e+10", 1.000055e+10f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.10000006e+38", 1.10000005e+38f); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.63529943e+10", 1.63529943e+10f); TEST_ASSERT_EQUAL_PRINT_FLOATING("3.40282347e+38", 3.40282346638e38f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1.0e+10", -1.0e+10f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-3.40282347e+38",-3.40282346638e38f); +#endif +} + +void testFloatPrintingRoundTiesToEven(void) +{ +#if defined(UNITY_EXCLUDE_FLOAT) || !defined(USING_OUTPUT_SPY) + TEST_IGNORE(); +#else + #ifdef UNITY_ROUND_TIES_AWAY_FROM_ZERO + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.007813", 0.0078125f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.976563", 0.9765625f); + #else /* Default to Round ties to even */ + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.007182", 0.0071825f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.976562", 0.9765625f); + #endif +#endif +} + +void testFloatPrintingInfinityAndNaN(void) +{ +#if defined(UNITY_EXCLUDE_FLOAT) || !defined(USING_OUTPUT_SPY) + TEST_IGNORE(); +#else TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 3.40282346638e38f*2.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.0f / f_zero); TEST_ASSERT_EQUAL_PRINT_FLOATING("-Inf", -3.40282346638e38f*2.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", -3.40282346638e38f*2.0f * f_zero); - //Double + TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", -3.40282346638e38f*2.0f * f_zero); +#endif +} + +void testDoublePrinting(void) +{ +#if 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("4294967295.999999", 4294967295.999999); TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967295.9999995); TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967296.0); TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 9999999995.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 10000000050.0); /*not if ties round away from 0*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719924e+15", 9007199245000000.0); /*not if ties round away from 0*/ TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719925e+15", 9007199254740990.0); TEST_ASSERT_EQUAL_PRINT_FLOATING("7.0e+100", 7.0e+100); + TEST_ASSERT_EQUAL_PRINT_FLOATING("3.0e+200", 3.0e+200); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.23456789e+300", 9.23456789e+300); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.100469", -0.10046949999999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4294967295.999999", -4294967295.999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.2949673e+09", -4294967295.9999995); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-7.0e+100", -7.0e+100); +#endif +} + +void testDoublePrintingRoundTiesToEven(void) +{ +#if 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("1.0e+10", 10000000050.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719924e+15", 9007199245000000.0); + #endif +#endif +} + +void testDoublePrintingInfinityAndNaN(void) +{ +#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY) + TEST_IGNORE(); +#else TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.7976931348623157e308*10.0); TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.0 / d_zero); TEST_ASSERT_EQUAL_PRINT_FLOATING("-Inf", -1.7976931348623157e308*10.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", -1.7976931348623157e308*10.0 * d_zero); #endif } From 25f641735155dfb702aa7517349a095616f6dfda Mon Sep 17 00:00:00 2001 From: jsalling Date: Mon, 14 Nov 2016 23:28:38 -0600 Subject: [PATCH 13/14] Refactor repeated code to print float expected and actual Move double tests down in the file --- src/unity.c | 49 +++++++------------- test/tests/testunity.c | 100 ++++++++++++++++++++--------------------- 2 files changed, 67 insertions(+), 82 deletions(-) diff --git a/src/unity.c b/src/unity.c index 48f0be3..6313cd5 100644 --- a/src/unity.c +++ b/src/unity.c @@ -694,6 +694,19 @@ void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, #define UNITY_NAN_CHECK 0 #endif +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + do { \ + UnityPrint(UnityStrExpected); \ + UnityPrintFloat(expected); \ + UnityPrint(UnityStrWas); \ + UnityPrintFloat(actual); \ + } while(0) +#else + #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + UnityPrint(UnityStrDelta) +#endif /* UNITY_EXCLUDE_FLOAT_PRINT */ + #ifndef UNITY_EXCLUDE_FLOAT static int UnityFloatsWithin(_UF delta, _UF expected, _UF actual) @@ -729,14 +742,7 @@ void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const _UF* expected, UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrElement); UnityPrintNumberUnsigned(num_elements - elements - 1); -#ifdef UNITY_FLOAT_VERBOSE - UnityPrint(UnityStrExpected); - UnityPrintFloat(*ptr_expected); - UnityPrint(UnityStrWas); - UnityPrintFloat(*ptr_actual); -#else - UnityPrint(UnityStrDelta); -#endif + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } @@ -758,14 +764,7 @@ void UnityAssertFloatsWithin(const _UF delta, if (!UnityFloatsWithin(delta, expected, actual)) { UnityTestResultsFailBegin(lineNumber); -#ifndef UNITY_EXCLUDE_FLOAT_PRINT - UnityPrint(UnityStrExpected); - UnityPrintFloat(expected); - UnityPrint(UnityStrWas); - UnityPrintFloat(actual); -#else - UnityPrint(UnityStrDelta); -#endif + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } @@ -872,14 +871,7 @@ void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const _UD* expected, UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrElement); UnityPrintNumberUnsigned(num_elements - elements - 1); -#ifndef UNITY_EXCLUDE_FLOAT_PRINT - UnityPrint(UnityStrExpected); - UnityPrintFloat(*ptr_expected); - UnityPrint(UnityStrWas); - UnityPrintFloat(*ptr_actual); -#else - UnityPrint(UnityStrDelta); -#endif + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } @@ -900,14 +892,7 @@ void UnityAssertDoublesWithin(const _UD delta, if (!UnityDoublesWithin(delta, expected, actual)) { UnityTestResultsFailBegin(lineNumber); -#ifndef UNITY_EXCLUDE_FLOAT_PRINT - UnityPrint(UnityStrExpected); - UnityPrintFloat(expected); - UnityPrint(UnityStrWas); - UnityPrintFloat(actual); -#else - UnityPrint(UnityStrDelta); -#endif + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } diff --git a/test/tests/testunity.c b/test/tests/testunity.c index 9a0f714..b9f6fdc 100644 --- a/test/tests/testunity.c +++ b/test/tests/testunity.c @@ -3313,56 +3313,6 @@ void testFloatPrintingInfinityAndNaN(void) #endif } -void testDoublePrinting(void) -{ -#if 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("4294967295.999999", 4294967295.999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967295.9999995); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967296.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 9999999995.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719925e+15", 9007199254740990.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("7.0e+100", 7.0e+100); - TEST_ASSERT_EQUAL_PRINT_FLOATING("3.0e+200", 3.0e+200); - TEST_ASSERT_EQUAL_PRINT_FLOATING("9.23456789e+300", 9.23456789e+300); - - TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.100469", -0.10046949999999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-4294967295.999999", -4294967295.999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.2949673e+09", -4294967295.9999995); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-7.0e+100", -7.0e+100); -#endif -} - -void testDoublePrintingRoundTiesToEven(void) -{ -#if 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("1.0e+10", 10000000050.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719924e+15", 9007199245000000.0); - #endif -#endif -} - -void testDoublePrintingInfinityAndNaN(void) -{ -#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY) - TEST_IGNORE(); -#else - TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.7976931348623157e308*10.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.0 / d_zero); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-Inf", -1.7976931348623157e308*10.0); - - TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", -1.7976931348623157e308*10.0 * d_zero); -#endif -} - // ===================== THESE TEST WILL RUN IF YOUR CONFIG INCLUDES DOUBLE SUPPORT ================== void testDoublesWithinDelta(void) @@ -3886,6 +3836,56 @@ void testNotEqualDoubleArraysLengthZero(void) #endif } +void testDoublePrinting(void) +{ +#if 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("4294967295.999999", 4294967295.999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967295.9999995); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967296.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 9999999995.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719925e+15", 9007199254740990.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("7.0e+100", 7.0e+100); + TEST_ASSERT_EQUAL_PRINT_FLOATING("3.0e+200", 3.0e+200); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.23456789e+300", 9.23456789e+300); + + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.100469", -0.10046949999999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4294967295.999999", -4294967295.999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.2949673e+09", -4294967295.9999995); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-7.0e+100", -7.0e+100); +#endif +} + +void testDoublePrintingRoundTiesToEven(void) +{ +#if 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("1.0e+10", 10000000050.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719924e+15", 9007199245000000.0); + #endif +#endif +} + +void testDoublePrintingInfinityAndNaN(void) +{ +#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY) + TEST_IGNORE(); +#else + TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.7976931348623157e308*10.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.0 / d_zero); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-Inf", -1.7976931348623157e308*10.0); + + TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", -1.7976931348623157e308*10.0 * d_zero); +#endif +} + // ===================== THESE TEST WILL RUN IF YOUR CONFIG INCLUDES DETAIL SUPPORT ================== void testThatDetailsCanBeHandleOneDetail(void) From aa4d773df28faf64f1d2964639abc7b510857346 Mon Sep 17 00:00:00 2001 From: jsalling Date: Wed, 16 Nov 2016 19:56:17 -0600 Subject: [PATCH 14/14] Tests for Printing All float values Takes about 10 minutes to run all floats, so split into 3 tests for parallel running later. This was useful during development for finding hard corner cases and getting the routine to high quality. Off by default. Note that all floats 16.0 and up can be represented uniquely (in this format) and will round-trip back to the exact same float with sscanf(). This property is true for UnityPrintFloat, despite a few rounding error cases, it will produce output identity. Better comments and refactor on round ties to even Add upper threshold value on round to even feature since numerical precision issues start to give approximations when dividing one large double by another. When tested on float values, using 1e22 gave the fewest rounding errors. Fix warnings from gcc. Some float constants do not behave well in existing tests. Add casts where conversions could be imprecise. --- src/unity.c | 35 ++++++----- test/tests/testunity.c | 129 +++++++++++++++++++++++++++++++++++------ 2 files changed, 132 insertions(+), 32 deletions(-) diff --git a/src/unity.c b/src/unity.c index 6313cd5..aceedb5 100644 --- a/src/unity.c +++ b/src/unity.c @@ -263,20 +263,27 @@ static void UnityPrintDecimalAndNumberWithLeadingZeros(_US32 fraction_part, _US3 } } #ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO - #define ROUND_TIES_TO_EVEN(num_int, num) \ - if ((num_int & 1) == 1 && num_int > (num)) /* Odd and was rounded up */ \ - if ((num) - (_US32)(num) <= 0.5) num_int -= 1 /* and remainder was 0.5, a tie */ +/* If rounds up && remainder 0.5 && result odd && below cutoff for double precision issues */ + #define ROUND_TIES_TO_EVEN(orig, num_int, num) \ + if (num_int > (num) && (num) - (num_int-1) <= 0.5 && (num_int & 1) == 1 && orig < 1e22) \ + num_int -= 1 /* => a tie to round down to even */ #else - #define ROUND_TIES_TO_EVEN(num_int, num) + #define ROUND_TIES_TO_EVEN(orig, num_int, num) /* Remove macro */ #endif -/* - * char buffer[19]; - * if (number > 4294967296.0 || -number > 4294967296.0) - * snprintf(buffer, sizeof buffer, "%.8e", number); - * else - * snprintf(buffer, sizeof buffer, "%.6f", number); - * UnityPrint(buffer); +/* Printing floating point numbers is hard. Some goals of this implementation: works for embedded + * systems, floats or doubles, and has a reasonable format. The key paper in this area, + * 'How to Print Floating-Point Numbers Accurately' by Steele & White, shows an approximation by + * scaling called Dragon 2. This code uses a similar idea. The other core algorithm uses casts and + * floating subtraction to give exact remainders after the decimal, to be scaled into an integer. + * Extra trailing 0's are excluded. The output defaults to rounding to nearest, ties to even. You + * can enable rounding ties away from zero. Note: the _UD parameter can typedef to float or double. + + * The old version required compiling in snprintf. For reference, with a similar format as now: + * char buf[19]; + * if (number > 4294967296.0 || -number > 4294967296.0) snprintf(buf, sizeof buf, "%.8e", number); + * else snprintf(buf, sizeof buf, "%.6f", number); + * UnityPrint(buf); */ void UnityPrintFloat(_UD number) { @@ -293,9 +300,9 @@ void UnityPrintFloat(_UD number) { const _US32 divisor = (1000000/10); _UU32 integer_part = (_UU32)number; - _US32 fraction_part = (_US32)((number - integer_part)*1000000.0 + 0.5); + _US32 fraction_part = (_US32)((number - (_UD)integer_part)*1000000.0 + 0.5); /* Double precision calculation gives best performance for six rounded decimal places */ - ROUND_TIES_TO_EVEN(fraction_part, (number - integer_part)*1000000.0); + ROUND_TIES_TO_EVEN(number, fraction_part, (number - (_UD)integer_part)*1000000.0); if (fraction_part == 1000000) /* Carry across the decimal point */ { @@ -320,7 +327,7 @@ void UnityPrintFloat(_UD number) } integer_part = (_US32)(number / divide + 0.5); /* Double precision calculation required for float, to produce 9 rounded digits */ - ROUND_TIES_TO_EVEN(integer_part, number / divide); + ROUND_TIES_TO_EVEN(number, integer_part, number / divide); UNITY_OUTPUT_CHAR('0' + integer_part / divisor); UnityPrintDecimalAndNumberWithLeadingZeros(integer_part % divisor, divisor / 10); diff --git a/test/tests/testunity.c b/test/tests/testunity.c index b9f6fdc..c52af67 100644 --- a/test/tests/testunity.c +++ b/test/tests/testunity.c @@ -5,7 +5,6 @@ ========================================== */ #include "unity.h" -#include #include #include @@ -61,19 +60,20 @@ static int SetToOneMeanWeAlreadyCheckedThisGuy; void setUp(void) { - SetToOneToFailInTearDown = 0; - SetToOneMeanWeAlreadyCheckedThisGuy = 0; + SetToOneToFailInTearDown = 0; + SetToOneMeanWeAlreadyCheckedThisGuy = 0; } void tearDown(void) { - if (SetToOneToFailInTearDown == 1) - TEST_FAIL_MESSAGE("<= Failed in tearDown"); - if ((SetToOneMeanWeAlreadyCheckedThisGuy == 0) && (Unity.CurrentTestFailed > 0)) - { - UnityPrint(": [[[[ Test Should Have Passed But Did Not ]]]]"); - UNITY_OUTPUT_CHAR('\n'); - } + endPutcharSpy(); /* Stop suppressing test output */ + if (SetToOneToFailInTearDown == 1) + TEST_FAIL_MESSAGE("<= Failed in tearDown"); + if ((SetToOneMeanWeAlreadyCheckedThisGuy == 0) && (Unity.CurrentTestFailed > 0)) + { + UnityPrint(": [[[[ Test Should Have Passed But Did Not ]]]]"); + UNITY_OUTPUT_CHAR('\n'); + } } void testUnitySizeInitializationReminder(void) @@ -2238,7 +2238,7 @@ void testIgnoredAndThenFailInTearDown(void) #endif #ifdef USING_OUTPUT_SPY -int putchar(int); +#include #define SPY_BUFFER_MAX 40 static char putcharSpyBuffer[SPY_BUFFER_MAX]; #endif @@ -2267,7 +2267,7 @@ void putcharSpy(int c) if (indexSpyBuffer < SPY_BUFFER_MAX - 1) putcharSpyBuffer[indexSpyBuffer++] = (char)c; } else - c = putchar(c); + putchar((char)c); #endif } @@ -3305,11 +3305,105 @@ void testFloatPrintingInfinityAndNaN(void) #if defined(UNITY_EXCLUDE_FLOAT) || !defined(USING_OUTPUT_SPY) TEST_IGNORE(); #else - TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 3.40282346638e38f*2.0f); TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.0f / f_zero); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-Inf", -3.40282346638e38f*2.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-Inf", -1.0f / f_zero); - TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", -3.40282346638e38f*2.0f * f_zero); + TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", 0.0f / f_zero); +#endif +} + +#if defined(UNITY_TEST_ALL_FLOATS_PRINT_OK) && defined(USING_OUTPUT_SPY) +static void AllFloatPrinting_LessThan32Bits(void) +{ + char expected[18]; + union { float f_value; int32_t int_value; } u; + /* Float representations are laid out in integer order, walk up the list */ + for (u.f_value = 0.00000050000005f; u.f_value <= 4294967040.0f; u.int_value += 1) + { + startPutcharSpy(); + + UnityPrintFloat(u.f_value); /*1.5x as fast as sprintf 5e-7f - 0.01f, 20s vs 30s*/ + int len = sprintf(expected, "%.6f", u.f_value); + + while (expected[len - 1] == '0' && expected[len - 2] != '.') { len--; } + expected[len] = '\0'; /* delete trailing 0's */ + + if (strcmp(expected, getBufferPutcharSpy()) != 0) + { + double six_digits = ((double)u.f_value - (uint32_t)u.f_value)*1000000.0; + /* Not a tie (remainder != 0.5) => Can't explain the different strings */ + if (six_digits - (uint32_t)six_digits != 0.5) + { + /* Fail with diagnostic printing */ + TEST_ASSERT_EQUAL_PRINT_FLOATING(expected, u.f_value); + } + } + } +} + +/* Compared to perfect, floats are occasionally rounded wrong. It doesn't affect + * correctness, though. Two examples (of 13 total found during testing): + * Printed: 6.19256349e+20, Exact: 619256348499999981568.0f <= Eliminated by ROUND_TIES_TO_EVEN + * Printed: 2.19012272e+35, Exact: 219012271499999993621766990196637696.0f */ +static void AllFloatPrinting_Larger(const float start, const float end) +{ + unsigned int wrong = 0; + char expected[18]; + union { float f_value; int32_t int_value; } u; + for (u.f_value = start; u.f_value <= end; u.int_value += 1) + { + startPutcharSpy(); + + UnityPrintFloat(u.f_value); /*Twice as fast as sprintf 2**32-1e12, 10s vs 21s*/ + sprintf(expected, "%.8e", u.f_value); + + int len = 11 - 1; /* 11th char is 'e' in exponential format */ + while (expected[len - 1] == '0' && expected[len - 2] != '.') { len --; } + if (expected[14] != '\0') memmove(&expected[12], &expected[13], 3); /* Two char exponent */ + memmove(&expected[len], &expected[11 - 1], sizeof "e+09"); /* 5 char length */ + + if (strcmp(expected, getBufferPutcharSpy()) != 0) + { + wrong++; + /* endPutcharSpy(); UnityPrint("Expected "); UnityPrint(expected); + UnityPrint(" Was "); UnityPrint(getBufferPutcharSpy()); UNITY_OUTPUT_CHAR('\n'); */ + + if (wrong > 10 || (wrong > 3 && end <= 1e25f)) + TEST_ASSERT_EQUAL_PRINT_FLOATING(expected, u.f_value); + /* Empirical values from the current routine, don't be worse when making changes */ + } + } +} +#endif + +/* Exhaustive testing of all float values we differentiate when printing. Doubles + * are not explored here -- too many. These tests confirm that the routine works + * for all floats > 5e-7, positives only. Off by default due to test time. + * Compares Unity's routine to your sprintf() C lib, tested to pass on 3 platforms. + * Part1 takes a long time, around 3 minutes compiled with -O2 + * Runs through all floats from 0.000001 - 2**32, ~300 million values */ +void testAllFloatPrintingPart1_LessThan32Bits(void) +{ +#if defined(UNITY_TEST_ALL_FLOATS_PRINT_OK) && defined(USING_OUTPUT_SPY) + AllFloatPrinting_LessThan32Bits(); +#else + TEST_IGNORE(); /* Ignore one of three */ +#endif +} + +/* Test takes a long time, around 3.5 minutes compiled with -O2, try ~500 million values */ +void testAllFloatPrintingPart2_Larger(void) +{ +#if defined(UNITY_TEST_ALL_FLOATS_PRINT_OK) && defined(USING_OUTPUT_SPY) + AllFloatPrinting_Larger(4294967296.0f, 1e25f); +#endif +} + +/* Test takes a long time, around 3.5 minutes compiled with -O2, try ~500 million values */ +void testAllFloatPrintingPart3_LargerStill(void) +{ +#if defined(UNITY_TEST_ALL_FLOATS_PRINT_OK) && defined(USING_OUTPUT_SPY) + AllFloatPrinting_Larger(1e25f, 3.40282347e+38f); #endif } @@ -3878,11 +3972,10 @@ void testDoublePrintingInfinityAndNaN(void) #if defined(UNITY_EXCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY) TEST_IGNORE(); #else - TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.7976931348623157e308*10.0); TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.0 / d_zero); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-Inf", -1.7976931348623157e308*10.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-Inf", -1.0 / d_zero); - TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", -1.7976931348623157e308*10.0 * d_zero); + TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", 0.0 / d_zero); #endif }