diff --git a/cJSON.c b/cJSON.c index 88c2d95b..4cbdf8b4 100644 --- a/cJSON.c +++ b/cJSON.c @@ -588,7 +588,17 @@ static void update_offset(printbuffer * const buffer) /* securely comparison of floating-point variables */ static cJSON_bool compare_double(double a, double b) { - double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + double maxVal = 0.0; + + /* the epsilon-based comparison below breaks down for infinities: a - b + * and maxVal both come out as +/-infinity, so it can't tell +inf from + * -inf, or notice that two separately computed +inf values match */ + if (isinf(a) || isinf(b)) + { + return (a == b); + } + + maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); return (fabs(a - b) <= maxVal * DBL_EPSILON); } diff --git a/cJSON_Utils.c b/cJSON_Utils.c index 8b38eb25..e957c08f 100644 --- a/cJSON_Utils.c +++ b/cJSON_Utils.c @@ -52,6 +52,14 @@ #include "cJSON_Utils.h" +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + /* define our own boolean type */ #ifdef true #undef true @@ -111,7 +119,17 @@ static int compare_strings(const unsigned char *string1, const unsigned char *st /* securely comparison of floating-point variables */ static cJSON_bool compare_double(double a, double b) { - double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + double maxVal = 0.0; + + /* the epsilon-based comparison below breaks down for infinities: a - b + * and maxVal both come out as +/-infinity, so it can't tell +inf from + * -inf, or notice that two separately computed +inf values match */ + if (isinf(a) || isinf(b)) + { + return (a == b); + } + + maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); return (fabs(a - b) <= maxVal * DBL_EPSILON); } diff --git a/tests/compare_tests.c b/tests/compare_tests.c index 797c7740..47383926 100644 --- a/tests/compare_tests.c +++ b/tests/compare_tests.c @@ -20,6 +20,8 @@ THE SOFTWARE. */ +#include + #include "unity/examples/unity_config.h" #include "unity/src/unity.h" #include "common.h" @@ -189,6 +191,34 @@ static void cjson_compare_should_compare_objects(void) false)) } +static void cjson_compare_should_compare_infinities_correctly(void) +{ + /* JSON text has no way to spell an infinite number, so build these + * directly instead of going through compare_from_string. */ + cJSON *positive_a = cJSON_CreateNumber(INFINITY); + cJSON *positive_b = cJSON_CreateNumber(INFINITY); + cJSON *negative_a = cJSON_CreateNumber(-INFINITY); + cJSON *negative_b = cJSON_CreateNumber(-INFINITY); + + TEST_ASSERT_NOT_NULL(positive_a); + TEST_ASSERT_NOT_NULL(positive_b); + TEST_ASSERT_NOT_NULL(negative_a); + TEST_ASSERT_NOT_NULL(negative_b); + + /* two separately created +Infinity values are equal to each other */ + TEST_ASSERT_TRUE(cJSON_Compare(positive_a, positive_b, true)); + /* two separately created -Infinity values are equal to each other */ + TEST_ASSERT_TRUE(cJSON_Compare(negative_a, negative_b, true)); + /* +Infinity and -Infinity are not equal */ + TEST_ASSERT_FALSE(cJSON_Compare(positive_a, negative_a, true)); + TEST_ASSERT_FALSE(cJSON_Compare(negative_a, positive_a, true)); + + cJSON_Delete(positive_a); + cJSON_Delete(positive_b); + cJSON_Delete(negative_a); + cJSON_Delete(negative_b); +} + int CJSON_CDECL main(void) { UNITY_BEGIN(); @@ -196,6 +226,7 @@ int CJSON_CDECL main(void) RUN_TEST(cjson_compare_should_compare_null_pointer_as_not_equal); RUN_TEST(cjson_compare_should_compare_invalid_as_not_equal); RUN_TEST(cjson_compare_should_compare_numbers); + RUN_TEST(cjson_compare_should_compare_infinities_correctly); RUN_TEST(cjson_compare_should_compare_booleans); RUN_TEST(cjson_compare_should_compare_null); RUN_TEST(cjson_compare_should_not_accept_invalid_types);