diff options
-rw-r--r-- | src/basic/macro.h | 4 | ||||
-rw-r--r-- | src/shared/json.c | 42 | ||||
-rw-r--r-- | src/test/test-json.c | 18 |
3 files changed, 27 insertions, 37 deletions
diff --git a/src/basic/macro.h b/src/basic/macro.h index bcac55620e..237117db12 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -87,10 +87,6 @@ _Pragma("GCC diagnostic push") #endif -#define DISABLE_WARNING_FLOAT_EQUAL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"") - #define DISABLE_WARNING_TYPE_LIMITS \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") diff --git a/src/shared/json.c b/src/shared/json.c index 13bc44a9ed..67b6b75fa8 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -2,7 +2,6 @@ #include <errno.h> #include <locale.h> -#include <math.h> #include <stdarg.h> #include <stdlib.h> #include <sys/types.h> @@ -18,6 +17,7 @@ #include "json-internal.h" #include "json.h" #include "macro.h" +#include "math-util.h" #include "memory-util.h" #include "string-table.h" #include "string-util.h" @@ -253,9 +253,7 @@ static JsonVariant *json_variant_formalize(JsonVariant *v) { return json_variant_unsigned(v) == 0 ? JSON_VARIANT_MAGIC_ZERO_UNSIGNED : v; case JSON_VARIANT_REAL: - DISABLE_WARNING_FLOAT_EQUAL; - return json_variant_real(v) == 0.0 ? JSON_VARIANT_MAGIC_ZERO_REAL : v; - REENABLE_WARNING; + return iszero_safe(json_variant_real(v)) ? JSON_VARIANT_MAGIC_ZERO_REAL : v; case JSON_VARIANT_STRING: return isempty(json_variant_string(v)) ? JSON_VARIANT_MAGIC_EMPTY_STRING : v; @@ -352,16 +350,16 @@ int json_variant_new_real(JsonVariant **ret, double d) { assert_return(ret, -EINVAL); - DISABLE_WARNING_FLOAT_EQUAL; - if (d == 0.0) { - *ret = JSON_VARIANT_MAGIC_ZERO_REAL; + r = fpclassify(d); + switch (r) { + case FP_NAN: + case FP_INFINITE: + /* JSON doesn't know NaN, +Infinity or -Infinity. Let's silently convert to 'null'. */ + *ret = JSON_VARIANT_MAGIC_NULL; return 0; - } - REENABLE_WARNING; - /* JSON doesn't know NaN, +Infinity or -Infinity. Let's silently convert to 'null'. */ - if (isnan(d) || isinf(d)) { - *ret = JSON_VARIANT_MAGIC_NULL; + case FP_ZERO: + *ret = JSON_VARIANT_MAGIC_ZERO_REAL; return 0; } @@ -914,10 +912,8 @@ int64_t json_variant_integer(JsonVariant *v) { converted = (int64_t) v->value.real; - DISABLE_WARNING_FLOAT_EQUAL; - if ((double) converted == v->value.real) + if (fp_equal((double) converted, v->value.real)) return converted; - REENABLE_WARNING; log_debug("Real %g requested as integer, and cannot be converted losslessly, returning 0.", v->value.real); return 0; @@ -961,10 +957,8 @@ uint64_t json_variant_unsigned(JsonVariant *v) { converted = (uint64_t) v->value.real; - DISABLE_WARNING_FLOAT_EQUAL; - if ((double) converted == v->value.real) + if (fp_equal((double) converted, v->value.real)) return converted; - REENABLE_WARNING; log_debug("Real %g requested as unsigned integer, and cannot be converted losslessly, returning 0.", v->value.real); return 0; @@ -1153,15 +1147,11 @@ bool json_variant_has_type(JsonVariant *v, JsonVariantType type) { if (rt == JSON_VARIANT_UNSIGNED && type == JSON_VARIANT_REAL) return (uint64_t) (double) v->value.unsig == v->value.unsig; - DISABLE_WARNING_FLOAT_EQUAL; - /* Any real that can be converted losslessly to an integer and back may also be considered an integer */ if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_INTEGER) - return (double) (int64_t) v->value.real == v->value.real; + return fp_equal((double) (int64_t) v->value.real, v->value.real); if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_UNSIGNED) - return (double) (uint64_t) v->value.real == v->value.real; - - REENABLE_WARNING; + return fp_equal((double) (uint64_t) v->value.real, v->value.real); return false; } @@ -1314,9 +1304,7 @@ bool json_variant_equal(JsonVariant *a, JsonVariant *b) { return json_variant_unsigned(a) == json_variant_unsigned(b); case JSON_VARIANT_REAL: - DISABLE_WARNING_FLOAT_EQUAL; - return json_variant_real(a) == json_variant_real(b); - REENABLE_WARNING; + return fp_equal(json_variant_real(a), json_variant_real(b)); case JSON_VARIANT_BOOLEAN: return json_variant_boolean(a) == json_variant_boolean(b); diff --git a/src/test/test-json.c b/src/test/test-json.c index 415ada22bf..d22485630a 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include <float.h> -#include <math.h> #include "alloc-util.h" #include "escape.h" @@ -9,6 +8,7 @@ #include "fileio.h" #include "json-internal.h" #include "json.h" +#include "math-util.h" #include "string-util.h" #include "strv.h" #include "tests.h" @@ -239,9 +239,7 @@ static void test_zeroes(JsonVariant *v) { assert_se(json_variant_integer(w) == 0); assert_se(json_variant_unsigned(w) == 0U); - DISABLE_WARNING_FLOAT_EQUAL; - assert_se(json_variant_real(w) == 0.0L); - REENABLE_WARNING; + assert_se(iszero_safe(json_variant_real(w))); assert_se(json_variant_is_integer(w)); assert_se(json_variant_is_unsigned(w)); @@ -511,7 +509,7 @@ static void test_float_match(JsonVariant *v) { const double delta = 0.0001; assert_se(json_variant_is_array(v)); - assert_se(json_variant_elements(v) == 9); + assert_se(json_variant_elements(v) == 11); assert_se(fabs(1.0 - (DBL_MIN / json_variant_real(json_variant_by_index(v, 0)))) <= delta); assert_se(fabs(1.0 - (DBL_MAX / json_variant_real(json_variant_by_index(v, 1)))) <= delta); assert_se(json_variant_is_null(json_variant_by_index(v, 2))); /* nan is not supported by json → null */ @@ -528,6 +526,12 @@ static void test_float_match(JsonVariant *v) { assert_se(json_variant_is_real(json_variant_by_index(v, 8)) && json_variant_is_integer(json_variant_by_index(v, 8)) && json_variant_integer(json_variant_by_index(v, 8)) == -10); + assert_se(json_variant_is_real(json_variant_by_index(v, 9)) && + !json_variant_is_integer(json_variant_by_index(v, 9))); + assert_se(fabs(1.0 - (DBL_MIN / 2 / json_variant_real(json_variant_by_index(v, 9)))) <= delta); + assert_se(json_variant_is_real(json_variant_by_index(v, 10)) && + !json_variant_is_integer(json_variant_by_index(v, 10))); + assert_se(fabs(1.0 - (-DBL_MIN / 2 / json_variant_real(json_variant_by_index(v, 10)))) <= delta); } TEST(float) { @@ -543,7 +547,9 @@ TEST(float) { JSON_BUILD_REAL(HUGE_VAL), JSON_BUILD_REAL(0), JSON_BUILD_REAL(10), - JSON_BUILD_REAL(-10))) >= 0); + JSON_BUILD_REAL(-10), + JSON_BUILD_REAL(DBL_MIN / 2), + JSON_BUILD_REAL(-DBL_MIN / 2))) >= 0); json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL); |