]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tree-wide: Use our own macros instead of fabs()/fmax()/fmin()
authorDaan De Meyer <daan@amutable.com>
Fri, 15 May 2026 11:06:21 +0000 (11:06 +0000)
committerDaan De Meyer <daan@amutable.com>
Mon, 18 May 2026 21:17:38 +0000 (21:17 +0000)
To make this work, ABS() is made generic so it also works on
floats and doubles.

While at it, fold the __ABS_INTEGER indirection and the
assert_cc(sizeof(long long) == sizeof(intmax_t)) away. The previous
form switched between __builtin_llabs (clang) and __builtin_imaxabs
(gcc), with the assert keeping the two paths behaviorally identical
on every platform we build for. imaxabs was originally chosen because
intmax_t is conceptually the widest signed integer type the platform
exposes, but the _Generic ABS already casts to (long long) before the
call, so the extra width imaxabs could in theory carry was being
narrowed away immediately anyway. With both paths collapsed to
__builtin_llabs((long long) (a)), the size relationship between
long long and intmax_t is no longer relevant.

Also add explicit unsigned long long / unsigned long / unsigned int
cases that pass the argument through unchanged. The previous default
branch cast unsigned values to (long long); for values above LLONG_MAX
this reinterprets them as negative, and __builtin_llabs(LLONG_MIN) is
UB. Unsigned values are already non-negative, so passing them through
is both correct and avoids the narrowing. Smaller unsigned types
(unsigned char, unsigned short) still go through the default branch
but promote to int first and fit in long long losslessly.

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
src/fundamental/macro-fundamental.h
src/libsystemd/sd-bus/test-bus-marshal.c
src/libsystemd/sd-json/test-json.c
src/shared/color-util.c
src/test/test-parse-util.c
src/test/test-random-util.c
src/timesync/timesyncd-manager.c

index 82e2cb4c3bca323cbfe547ea44b8f8cf3946374a..12f2aa81dcd8347e4744c6dfe3908bbea2e37cdd 100644 (file)
                 UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
         })
 
-#ifdef __clang__
-#  define ABS(a) __builtin_llabs(a)
-#else
-#  define ABS(a) __builtin_imaxabs(a)
-#endif
-assert_cc(sizeof(long long) == sizeof(intmax_t));
+#define ABS(a) _Generic((a),                                            \
+                float:              __builtin_fabsf((float) (a)),       \
+                double:             __builtin_fabs((double) (a)),       \
+                long double:        __builtin_fabsl((long double) (a)), \
+                unsigned long long: (a),                                \
+                unsigned long:      (a),                                \
+                unsigned int:       (a),                                \
+                default:            __builtin_llabs((long long) (a)))
 
 #define IS_UNSIGNED_INTEGER_TYPE(type) \
         (__builtin_types_compatible_p(typeof(type), unsigned char) ||   \
index 8229eef54ab9f9ea0f6633ef10982d9e879f9e50..5c2fca0f8357ee833dc044027895180a3a759db0 100644 (file)
@@ -1,7 +1,5 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <math.h>
-
 /* We make an exception here to our usual "include system headers first" rule because we need one of these
  * macros to disable a warning triggered by the glib headers. */
 #include "macro-fundamental.h"
@@ -479,7 +477,7 @@ int main(int argc, char *argv[]) {
         assert_se(r > 0);
         assert_se(streq(x, "foo"));
         assert_se(u64 == 815ULL);
-        assert_se(fabs(dbl - 47.0) < 0.1);
+        assert_se(ABS(dbl - 47.0) < 0.1);
         assert_se(streq(y, "/"));
 
         r = sd_bus_message_peek_type(m, NULL, NULL);
index 3be4b09660b140157c2dbd1cab326abf819d89b7..59724b00c39a5731a91671545f8f63daace90a53 100644 (file)
@@ -60,8 +60,8 @@ static void test_tokenizer_one(const char *data, ...) {
 
                         d = va_arg(ap, double);
 
-                        assert_se(fabs(d - v.real) < 1e-10 ||
-                                  fabs((d - v.real) / v.real) < 1e-10);
+                        assert_se(ABS(d - v.real) < 1e-10 ||
+                                  ABS((d - v.real) / v.real) < 1e-10);
 
                 } else if (t == JSON_TOKEN_INTEGER) {
                         int64_t i;
@@ -242,7 +242,7 @@ static void test_2(sd_json_variant *v) {
 
         /* has thisisaverylongproperty */
         p = sd_json_variant_by_key(v, "thisisaverylongproperty");
-        assert_se(p && sd_json_variant_type(p) == SD_JSON_VARIANT_REAL && fabs(sd_json_variant_real(p) - 1.27) < 0.001);
+        assert_se(p && sd_json_variant_type(p) == SD_JSON_VARIANT_REAL && ABS(sd_json_variant_real(p) - 1.27) < 0.001);
 }
 
 static void test_zeroes(sd_json_variant *v) {
@@ -690,14 +690,14 @@ static void test_float_match(sd_json_variant *v) {
         assert_se(sd_json_variant_is_array(v));
         assert_se(sd_json_variant_elements(v) == 11);
         assert_se(!iszero_safe(sd_json_variant_real(sd_json_variant_by_index(v, 0))));
-        assert_se(fabs(1.0 - (DBL_MIN / sd_json_variant_real(sd_json_variant_by_index(v, 0)))) <= delta);
+        assert_se(ABS(1.0 - (DBL_MIN / sd_json_variant_real(sd_json_variant_by_index(v, 0)))) <= delta);
         assert_se(!iszero_safe(sd_json_variant_real(sd_json_variant_by_index(v, 1))));
-        assert_se(fabs(1.0 - (DBL_MAX / sd_json_variant_real(sd_json_variant_by_index(v, 1)))) <= delta);
+        assert_se(ABS(1.0 - (DBL_MAX / sd_json_variant_real(sd_json_variant_by_index(v, 1)))) <= delta);
         assert_se(sd_json_variant_is_null(sd_json_variant_by_index(v, 2))); /* nan is not supported by json → null */
         assert_se(sd_json_variant_is_null(sd_json_variant_by_index(v, 3))); /* +inf is not supported by json → null */
         assert_se(sd_json_variant_is_null(sd_json_variant_by_index(v, 4))); /* -inf is not supported by json → null */
         assert_se(sd_json_variant_is_null(sd_json_variant_by_index(v, 5)) ||
-                  fabs(1.0 - (HUGE_VAL / sd_json_variant_real(sd_json_variant_by_index(v, 5)))) <= delta); /* HUGE_VAL might be +inf, but might also be something else */
+                  ABS(1.0 - (HUGE_VAL / sd_json_variant_real(sd_json_variant_by_index(v, 5)))) <= delta); /* HUGE_VAL might be +inf, but might also be something else */
         assert_se(sd_json_variant_is_real(sd_json_variant_by_index(v, 6)) &&
                   sd_json_variant_is_integer(sd_json_variant_by_index(v, 6)) &&
                   sd_json_variant_integer(sd_json_variant_by_index(v, 6)) == 0);
@@ -710,11 +710,11 @@ static void test_float_match(sd_json_variant *v) {
         assert_se(sd_json_variant_is_real(sd_json_variant_by_index(v, 9)) &&
                   !sd_json_variant_is_integer(sd_json_variant_by_index(v, 9)));
         assert_se(!iszero_safe(sd_json_variant_real(sd_json_variant_by_index(v, 9))));
-        assert_se(fabs(1.0 - (DBL_MIN / 2 / sd_json_variant_real(sd_json_variant_by_index(v, 9)))) <= delta);
+        assert_se(ABS(1.0 - (DBL_MIN / 2 / sd_json_variant_real(sd_json_variant_by_index(v, 9)))) <= delta);
         assert_se(sd_json_variant_is_real(sd_json_variant_by_index(v, 10)) &&
                   !sd_json_variant_is_integer(sd_json_variant_by_index(v, 10)));
         assert_se(!iszero_safe(sd_json_variant_real(sd_json_variant_by_index(v, 10))));
-        assert_se(fabs(1.0 - (-DBL_MIN / 2 / sd_json_variant_real(sd_json_variant_by_index(v, 10)))) <= delta);
+        assert_se(ABS(1.0 - (-DBL_MIN / 2 / sd_json_variant_real(sd_json_variant_by_index(v, 10)))) <= delta);
 }
 
 TEST(float) {
@@ -1080,8 +1080,8 @@ TEST(json_dispatch_double) {
                                 /* flags= */ 0,
                                 &data) >= 0);
 
-        assert_se(fabs(data.x1 - 0.5) < 0.01);
-        assert_se(fabs(data.x2 + 0.5) < 0.01);
+        assert_se(ABS(data.x1 - 0.5) < 0.01);
+        assert_se(ABS(data.x2 + 0.5) < 0.01);
         assert_se(isinf(data.x3));
         assert_se(data.x3 > 0);
         assert_se(isinf(data.x4));
index ed417a1a2010a3bf7b5a63e8ede037190fb6ae1a..f2add33b1d85b3293c7c6fb07c6d3d3771b6f23c 100644 (file)
@@ -11,8 +11,8 @@ void rgb_to_hsv(double r, double g, double b,
         assert(g >= 0 && g <= 1);
         assert(b >= 0 && b <= 1);
 
-        double max_color = fmax(r, fmax(g, b));
-        double min_color = fmin(r, fmin(g, b));
+        double max_color = MAX(r, MAX(g, b));
+        double min_color = MIN(r, MIN(g, b));
         double delta = max_color - min_color;
 
         if (ret_v)
index 0c2cc500b13b07d8e562a830a762de48565e6010..bc74856fde6296259f1d35816d895299b225a458 100644 (file)
@@ -2,7 +2,6 @@
 
 #include <linux/netfilter/nf_tables.h>
 #include <locale.h>
-#include <math.h>
 #include <sys/socket.h>
 
 #include "capability-util.h"
@@ -660,7 +659,7 @@ TEST(safe_atod) {
         ASSERT_ERROR(safe_atod("junk", &d), EINVAL);
 
         ASSERT_OK_ZERO(safe_atod("0.2244", &d));
-        assert_se(fabs(d - 0.2244) < 0.000001);
+        assert_se(ABS(d - 0.2244) < 0.000001);
 
         ASSERT_ERROR(safe_atod("0,5", &d), EINVAL);
         ASSERT_ERROR(safe_atod("", &d), EINVAL);
@@ -672,7 +671,7 @@ TEST(safe_atod) {
                 return (void) log_tests_skipped_errno(errno, "locale de_DE.utf8 not found");
 
         ASSERT_OK_ZERO(safe_atod("0.2244", &d));
-        assert_se(fabs(d - 0.2244) < 0.000001);
+        assert_se(ABS(d - 0.2244) < 0.000001);
 
         ASSERT_ERROR(safe_atod("0,5", &d), EINVAL);
         ASSERT_ERROR(safe_atod("", &d), EINVAL);
index e5972718b46ff5e9e9a037f7571b57183981f711..d5043779b702af0258de17068a1345d5b8d95d3e 100644 (file)
@@ -65,7 +65,7 @@ static void test_random_u64_range_one(unsigned mod) {
                           i, count[i], dev,
                           (int) (count[i] / scale), "x");
 
-                assert_se(fabs(dev) < 6); /* 6 sigma is excessive, but this check should be enough to
+                assert_se(ABS(dev) < 6); /* 6 sigma is excessive, but this check should be enough to
                                            * identify catastrophic failure while minimizing false
                                            * positives. */
         }
index e4f471e2aa5dcf65228e15d4d83dd2bf9ff7976b..b0f22516dfe3306735b904e37ba1030a28f2155d 100644 (file)
@@ -244,7 +244,7 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
 
         /* For small deltas, tell the kernel to gradually adjust the system clock to the NTP time, larger
          * deltas are just directly set. */
-        if (fabs(offset) < NTP_MAX_ADJUST) {
+        if (ABS(offset) < NTP_MAX_ADJUST) {
                 tmx = (struct timex) {
                         .modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR,
                         .status = STA_PLL,
@@ -346,7 +346,7 @@ static bool manager_sample_spike_detection(Manager *m, double offset, double del
                 return false;
 
         /* always accept offset if we are farther off than the round-trip delay */
-        if (fabs(offset) > delay)
+        if (ABS(offset) > delay)
                 return false;
 
         /* we need a few samples before looking at them */
@@ -354,11 +354,11 @@ static bool manager_sample_spike_detection(Manager *m, double offset, double del
                 return false;
 
         /* do not accept anything worse than the maximum possible error of the best sample */
-        if (fabs(offset) > m->samples[idx_min].delay)
+        if (ABS(offset) > m->samples[idx_min].delay)
                 return true;
 
         /* compare the difference between the current offset to the previous offset and jitter */
-        return fabs(offset - m->samples[idx_cur].offset) > 3 * jitter;
+        return ABS(offset - m->samples[idx_cur].offset) > 3 * jitter;
 }
 
 static void manager_adjust_poll(Manager *m, double offset, bool spike) {
@@ -371,20 +371,20 @@ static void manager_adjust_poll(Manager *m, double offset, bool spike) {
         }
 
         /* set to minimal poll interval */
-        if (!spike && fabs(offset) > NTP_ACCURACY_SEC) {
+        if (!spike && ABS(offset) > NTP_ACCURACY_SEC) {
                 m->poll_interval_usec = m->poll_interval_min_usec;
                 return;
         }
 
         /* increase polling interval */
-        if (fabs(offset) < NTP_ACCURACY_SEC * 0.25) {
+        if (ABS(offset) < NTP_ACCURACY_SEC * 0.25) {
                 if (m->poll_interval_usec < m->poll_interval_max_usec)
                         m->poll_interval_usec *= 2;
                 return;
         }
 
         /* decrease polling interval */
-        if (spike || fabs(offset) > NTP_ACCURACY_SEC * 0.75) {
+        if (spike || ABS(offset) > NTP_ACCURACY_SEC * 0.75) {
                 if (m->poll_interval_usec > m->poll_interval_min_usec)
                         m->poll_interval_usec /= 2;
                 return;