Files
systemd/src/basic/math-util.h
T
Daan De Meyer 56bc502f6f math-util: round to declared FP precision consistently across architectures
Add -fexcess-precision=standard so gcc inserts ISO C99 conformant
rounding at assignments, casts, and returns — without it, double values
on x87 happily stay at 80-bit extended precision across operations and
diverge from the SSE/x86_64 behavior, making strict equality comparisons
architecture-dependent.

The flag doesn't fully cover x87: per gcc PR#323
(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323), a function return
value carried in ST(0) can arrive at the caller still at 80-bit, so a
double that ought to compare equal to a same-magnitude literal picks up
extra mantissa bits and doesn't. Wrap fp_equal in volatile-double
temporaries to force a memory roundtrip — the only operation that
reliably truncates on x87 — so its callers get consistent results
regardless of how the operands were produced.

Add a TEST(fp_equal) case that exercises the previously-broken pattern:
a runtime 1.0/10.0 computed inside a noinline helper, returned across
the function ABI boundary, then compared against the literal 0.1.
Without the volatile truncation this assertion fails on 32-bit gcc.
2026-05-21 11:53:32 +01:00

34 lines
1.9 KiB
C

/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <math.h> /* IWYU pragma: export */
#include "basic-forward.h"
/* On some optimization level, iszero(x) is converted to (x == 0.0), and emits warning -Wfloat-equal.
* The argument must be a floating point, i.e. one of float, double, or long double. */
#define iszero_safe(x) (fpclassify(x) == FP_ZERO)
/* To avoid x == y and triggering compile warning -Wfloat-equal. This returns false if one of the argument is
* NaN or infinity. One of the argument must be a floating point.
*
* The volatile temporaries force a memory roundtrip, truncating any excess precision (e.g. x87's
* 80-bit register width for double arithmetic) down to the declared type. -fexcess-precision=standard
* doesn't fully cover this on x87 — a function return value carried in ST(0) can still arrive at the
* caller in 80-bit precision (see gcc PR#323), so a value that should compare equal to a
* same-magnitude literal picks up extra mantissa bits and doesn't. The memory store-and-reload is
* the one operation guaranteed to truncate. The temporaries inherit the type of the subtraction
* expression so the macro stays generic over float / double / long double rather than silently
* truncating wider arguments. */
#define fp_equal(x, y) \
({ \
volatile __typeof__((x) - (y)) _fp_x = (x); \
volatile __typeof__((x) - (y)) _fp_y = (y); \
iszero_safe(_fp_x - _fp_y); \
})
/* 10^n. Exact for |n| ≤ 22; otherwise multiplies and may accumulate rounding error. Saturates to
* 0.0 or +Inf outside binary64's exponent range; large |n| is capped internally so untrusted
* inputs can't cause unbounded work. */
double xexp10i(int n);