From 74ba70283a026cfcb83cf8a1998c6727535ab5a4 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 14 Sep 2017 19:19:49 -0400 Subject: [PATCH] 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 &&