[PATCH v2] Fix ldbl-128ibm ceill, floorl, roundl and truncl zero-sign handling
Aurelien Jarno
aurelien@aurel32.net
Tue Jan 20 17:25:18 GMT 2026
When the result of ceill, floorl, roundl and truncl is zero, the sign of
the result must match the sign of the input. For the IBM 128-bit long
double format, the sign is determined by the high part.
Ensure the correct sign when the high part is the result of
computations, by copying the sign from the input high part to the output
high part. On POWER, this conveniently maps to the fcpsgn instruction.
In addition add test for the values provided in BZ #33623, and for the
opposite value when the result is 0.
Fixes: BZ #33623
---
math/libm-test-ceil.inc | 3 +++
math/libm-test-floor.inc | 3 +++
math/libm-test-round.inc | 4 ++++
math/libm-test-trunc.inc | 4 ++++
sysdeps/ieee754/ldbl-128ibm/s_ceill.c | 3 +++
sysdeps/ieee754/ldbl-128ibm/s_floorl.c | 3 +++
sysdeps/ieee754/ldbl-128ibm/s_roundl.c | 3 +++
sysdeps/ieee754/ldbl-128ibm/s_truncl.c | 3 +++
8 files changed, 26 insertions(+)
Changes from v1:
- add tests
- use the sign of 'hi' instead of keeping the old value in 'orig_xh'
- using copysign directly instead of __builtin_copysign
Thanks to Adhemerval and Joseph for the review and the suggestions on
v1.
For ceill, floorl and truncl, this got broken as part of the refactoring
in the following commits:
- commit e2310a27bede ("Fix ldbl-128ibm truncl for non-default
rounding modes (bug 19593).")
- commit 8a9fa0086dfe ("Fix ldbl-128ibm ceill for non-default
rounding modes (bug 19592).")
- commit 1833769e1923 ("Fix ldbl-128ibm floorl for non-default
rounding modes (bug 17899).")
Previously, the code used a comparison against zero to determine whether
the input was negative, in which case the result was produced as
-__builtin_fabs (xh).
For roundl, it seems it has always been broken.
This fixes the following tests added in 2.43 on PowerPC64LE:
- FAIL: math/test-ldouble-fromfp
- FAIL: math/test-ldouble-fromfpx
- FAIL: math/test-ldouble-ufromfp
- FAIL: math/test-ldouble-ufromfpx
I tested that without the fix the new tests causes the following failures on
PowerPC64LE:
- FAIL: math/test-ibm128-ceil
- FAIL: math/test-ibm128-floor
- FAIL: math/test-ibm128-round
- FAIL: math/test-ibm128-trunc
Due to the new tests and the close release, I tested this change for regression
on:
- aarch64-linux-gnu
- arm-linux-gnueabihf
- i686-linux-gnu
- powerpc64le-linux-gnu
- riscv64-linux-gnu
- s390x-linux-gnu
- x86_64-linux-gnu
diff --git a/math/libm-test-ceil.inc b/math/libm-test-ceil.inc
index a471b43932..48be68cf91 100644
--- a/math/libm-test-ceil.inc
+++ b/math/libm-test-ceil.inc
@@ -79,6 +79,9 @@ static const struct test_f_f_data ceil_test_data[] =
TEST_f_f (ceil, -max_value, -max_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
#if MANT_DIG >= 64
+ /* The input value can only be represented in long double. */
+ TEST_f_f (ceil, -0x0.ffffffffffffffffp0L, minus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+
/* The result can only be represented in long double. */
TEST_f_f (ceil, 4503599627370495.5L, 4503599627370496.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
TEST_f_f (ceil, 4503599627370496.25L, 4503599627370497.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
diff --git a/math/libm-test-floor.inc b/math/libm-test-floor.inc
index dee4afff7a..bc945a6c22 100644
--- a/math/libm-test-floor.inc
+++ b/math/libm-test-floor.inc
@@ -79,6 +79,9 @@ static const struct test_f_f_data floor_test_data[] =
TEST_f_f (floor, -max_value, -max_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
#if MANT_DIG >= 64
+ /* The input value can only be represented in long double. */
+ TEST_f_f (floor, 0x0.ffffffffffffffffp0L, plus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+
/* The result can only be represented in long double. */
TEST_f_f (floor, 4503599627370495.5L, 4503599627370495.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
TEST_f_f (floor, 4503599627370496.25L, 4503599627370496.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
diff --git a/math/libm-test-round.inc b/math/libm-test-round.inc
index 898ff3791b..1395c51d5d 100644
--- a/math/libm-test-round.inc
+++ b/math/libm-test-round.inc
@@ -99,6 +99,10 @@ static const struct test_f_f_data round_test_data[] =
#endif
#if MANT_DIG >= 64
+ /* The input value can only be represented in long double. */
+ TEST_f_f (round, 0x0.ffffffffffffffffp-1L, plus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+ TEST_f_f (round, -0x0.ffffffffffffffffp-1L, minus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+
/* The result can only be represented in long double. */
TEST_f_f (round, 4503599627370495.5L, 4503599627370496.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
TEST_f_f (round, 4503599627370496.25L, 4503599627370496.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
diff --git a/math/libm-test-trunc.inc b/math/libm-test-trunc.inc
index 7df741f3ba..5d69cd4b5b 100644
--- a/math/libm-test-trunc.inc
+++ b/math/libm-test-trunc.inc
@@ -91,6 +91,10 @@ static const struct test_f_f_data trunc_test_data[] =
TEST_f_f (trunc, -4294967296.625L, -4294967296.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
#if MANT_DIG >= 64
+ /* The input value can only be represented in long double. */
+ TEST_f_f (trunc, 0x0.ffffffffffffffffp0L, plus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+ TEST_f_f (trunc, -0x0.ffffffffffffffffp0L, minus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+
/* The result can only be represented in long double. */
TEST_f_f (trunc, 4503599627370495.5L, 4503599627370495.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
TEST_f_f (trunc, 4503599627370496.25L, 4503599627370496.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
diff --git a/sysdeps/ieee754/ldbl-128ibm/s_ceill.c b/sysdeps/ieee754/ldbl-128ibm/s_ceill.c
index 8038cf9fda..88bc364db5 100644
--- a/sysdeps/ieee754/ldbl-128ibm/s_ceill.c
+++ b/sysdeps/ieee754/ldbl-128ibm/s_ceill.c
@@ -54,6 +54,9 @@ __ceill (long double x)
xh = hi;
xl = lo;
ldbl_canonicalize_int (&xh, &xl);
+
+ /* Ensure we return -0 rather than +0 when appropriate. */
+ xh = copysign (xh, hi);
}
}
else
diff --git a/sysdeps/ieee754/ldbl-128ibm/s_floorl.c b/sysdeps/ieee754/ldbl-128ibm/s_floorl.c
index 23f45b773f..4a8294cc78 100644
--- a/sysdeps/ieee754/ldbl-128ibm/s_floorl.c
+++ b/sysdeps/ieee754/ldbl-128ibm/s_floorl.c
@@ -54,6 +54,9 @@ __floorl (long double x)
xh = hi;
xl = lo;
ldbl_canonicalize_int (&xh, &xl);
+
+ /* Ensure we return -0 rather than +0 when appropriate. */
+ xh = copysign (xh, hi);
}
}
else
diff --git a/sysdeps/ieee754/ldbl-128ibm/s_roundl.c b/sysdeps/ieee754/ldbl-128ibm/s_roundl.c
index e8948a0c05..340a63c76a 100644
--- a/sysdeps/ieee754/ldbl-128ibm/s_roundl.c
+++ b/sysdeps/ieee754/ldbl-128ibm/s_roundl.c
@@ -80,6 +80,9 @@ __roundl (long double x)
xh = hi;
ldbl_canonicalize_int (&xh, &xl);
}
+
+ /* Ensure we return -0 rather than +0 when appropriate. */
+ xh = copysign(xh, hi);
}
else
/* Quiet signaling NaN arguments. */
diff --git a/sysdeps/ieee754/ldbl-128ibm/s_truncl.c b/sysdeps/ieee754/ldbl-128ibm/s_truncl.c
index 7f0b77cd3f..21ecbd0c85 100644
--- a/sysdeps/ieee754/ldbl-128ibm/s_truncl.c
+++ b/sysdeps/ieee754/ldbl-128ibm/s_truncl.c
@@ -56,6 +56,9 @@ __truncl (long double x)
xh = hi;
xl = lo;
ldbl_canonicalize_int (&xh, &xl);
+
+ /* Ensure we return -0 rather than +0 when appropriate. */
+ xh = copysign (xh, hi);
}
}
else
--
2.51.0
More information about the Libc-alpha
mailing list