]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
math: Order signed zeros in f{max,min}{f,l,f128}
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>
Fri, 23 Jan 2026 13:02:20 +0000 (10:02 -0300)
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>
Mon, 2 Feb 2026 17:35:19 +0000 (14:35 -0300)
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 <jyknight@google.com>
Reviewed-by: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
manual/arith.texi
math/libm-test-fmax.inc
math/libm-test-fmin.inc
math/s_fmax_template.c
math/s_fmin_template.c

index a344153d24390e4bf1213275ee14a383276cc48f..7846b6903ecf0e10fa9985c08f01af84f2fc67cb 100644 (file)
@@ -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.
 
index 3d306004a77ab9228c3502a7af9cc838cc67129a..ecebdaf4ca447f19c65f018094c83410db742a63 100644 (file)
@@ -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),
index 9c8d1209ea827fa7469f1e0dfbcec8e53b16ce43..24de456f852ce92bc38b600524f833510e728813 100644 (file)
@@ -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),
index 5921d2b43cd3a875aa67fc09acf8fde946fbd502..8e3fde4af19606ee68da5e05c5c660d48744c0c4 100644 (file)
@@ -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
index e78358c8edcfedb8d4920f55ecda05c461e9a954..0ed32cd06cb4c7ec1254d6171ad1d4eaed3f31bb 100644 (file)
@@ -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