From: Adhemerval Zanella Date: Fri, 23 Jan 2026 13:02:20 +0000 (-0300) Subject: math: Order signed zeros in f{max,min}{f,l,f128} X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bdb07a03fb2f7601ecba7264adb452d9355a93ef;p=thirdparty%2Fglibc.git math: Order signed zeros in f{max,min}{f,l,f128} The C standard (at least from C99 until C23) does not require fmin/fmax to order zeros by their sign, so glibc's previous behavior was entirely standards-conforming. However, the standard does recommend that zeros be ordered in a footnote, saying: "If possible, fmax is sensitive to the sign of zero, for example fmax(−0.0, +0.0) ideally returns +0." As this is indeed possible (and not too complicated), implement it as a quality-of-implementation improvement. It also remove possible deviations between architectures, where for some architectures that has direct mapping instruction (USE_FMA*_BUILTIN) they already do the ordering. Checked on x86_64-linux-gnu, aarch64-linux-gnu, i686-linux-gnu, arm-linux-gnueabihf, powerpc64le-linux-gnu, riscv64-linux-gnu-rv64imafdc-lp64d, and loongarch64-linux-gnuf64. Co-authored-by: James Y Knight Reviewed-by: Wilco Dijkstra --- diff --git a/manual/arith.texi b/manual/arith.texi index a344153d24..7846b6903e 100644 --- a/manual/arith.texi +++ b/manual/arith.texi @@ -2110,9 +2110,12 @@ perform these operations faster than the equivalent C code. @standardsx{fminfNx, TS 18661-3:2015, math.h} @safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}} The @code{fmin} function returns the lesser of the two values @var{x} -and @var{y}. It is similar to the expression +and @var{y} respecting the order signed zeros. It is similar to the +expression @smallexample -((x) < (y) ? (x) : (y)) +if (x == y) + return signbit (x) ? x : y; +return ((x) < (y) ? (x) : (y)) @end smallexample except that @var{x} and @var{y} are only evaluated once. diff --git a/math/libm-test-fmax.inc b/math/libm-test-fmax.inc index 3d306004a7..ecebdaf4ca 100644 --- a/math/libm-test-fmax.inc +++ b/math/libm-test-fmax.inc @@ -22,8 +22,10 @@ static const struct test_ff_f_data fmax_test_data[] = { TEST_ff_f (fmax, 0, 0, 0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (fmax, minus_zero, minus_zero, minus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), - TEST_ff_f (fmax, 0, minus_zero, 0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED|IGNORE_ZERO_INF_SIGN), - TEST_ff_f (fmax, minus_zero, 0, 0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED|IGNORE_ZERO_INF_SIGN), + /* The order signed zeros are implemented as a QoI in the generic implementation + and expected for USE_F{MIN,MAX}{F,L,F128}_BUILTIN. */ + TEST_ff_f (fmax, 0, minus_zero, 0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), + TEST_ff_f (fmax, minus_zero, 0, 0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (fmax, min_subnorm_value, -min_subnorm_value, min_subnorm_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (fmax, -min_subnorm_value, min_subnorm_value, min_subnorm_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (fmax, min_value, -min_value, min_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), diff --git a/math/libm-test-fmin.inc b/math/libm-test-fmin.inc index 9c8d1209ea..24de456f85 100644 --- a/math/libm-test-fmin.inc +++ b/math/libm-test-fmin.inc @@ -22,8 +22,10 @@ static const struct test_ff_f_data fmin_test_data[] = { TEST_ff_f (fmin, 0, 0, 0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (fmin, minus_zero, minus_zero, minus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), - TEST_ff_f (fmin, 0, minus_zero, 0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED|IGNORE_ZERO_INF_SIGN), - TEST_ff_f (fmin, minus_zero, 0, 0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED|IGNORE_ZERO_INF_SIGN), + /* The order signed zeros are implemented as a QoI in the generic implementation + and expected for USE_F{MIN,MAX}{F,L,F128}_BUILTIN. */ + TEST_ff_f (fmin, 0, minus_zero, minus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), + TEST_ff_f (fmin, minus_zero, 0, minus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (fmin, min_subnorm_value, -min_subnorm_value, -min_subnorm_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (fmin, -min_subnorm_value, min_subnorm_value, -min_subnorm_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (fmin, min_value, -min_value, -min_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), diff --git a/math/s_fmax_template.c b/math/s_fmax_template.c index 5921d2b43c..8e3fde4af1 100644 --- a/math/s_fmax_template.c +++ b/math/s_fmax_template.c @@ -25,7 +25,11 @@ M_DECL_FUNC (__fmax) (FLOAT x, FLOAT y) return M_SUF (__builtin_fmax) (x, y); #else if (__glibc_likely (!isunordered (x, y))) - return x > y ? x : y; + { + if (__glibc_unlikely (x == y)) + return signbit (x) ? y : x; + return x > y ? x : y; + } else if (issignaling (x) || issignaling (y)) return x + y; else diff --git a/math/s_fmin_template.c b/math/s_fmin_template.c index e78358c8ed..0ed32cd06c 100644 --- a/math/s_fmin_template.c +++ b/math/s_fmin_template.c @@ -25,7 +25,11 @@ M_DECL_FUNC (__fmin) (FLOAT x, FLOAT y) return M_SUF (__builtin_fmin) (x, y); #else if (__glibc_likely (!isunordered (x, y))) - return x > y ? y : x; + { + if (__glibc_unlikely (x == y)) + return signbit (x) ? x : y; + return x > y ? y : x; + } else if (issignaling (x) || issignaling (y)) return x + y; else