[PATCH v2] math: Fix errno setting for narrowing sqrt functions on underflow [BZ #33393]
Osama Abdelkader
osama.abdelkader@gmail.com
Sun Oct 19 19:07:52 GMT 2025
The CHECK_NARROW_SQRT macro in math-narrow.h was not setting errno to
ERANGE when an underflow exception occurred during narrowing square root
operations. While the FE_UNDERFLOW exception flag was correctly set, errno
remained unchanged, violating IEEE 754 and POSIX requirements.
This fix adds a check for subnormal results, which indicate underflow has
occurred. When the result is non-zero but not normal (i.e., subnormal),
errno is set to ERANGE. This approach is more reliable than checking
floating-point exception flags, as the exception state may be modified by
the ROUND_TO_ODD macro before the check.
The issue affects all narrowing square root functions that use this macro:
- fsqrt (double to float)
- fsqrtl (long double to float)
- dsqrtl (long double to double)
A new test case (test-narrow-sqrt-errno) has been added to verify that
errno is correctly set to ERANGE when underflow occurs in narrowing sqrt
operations.
Tested on x86_64-linux-gnu. All tests pass.
Signed-off-by: Osama Abdelkader <osama.abdelkader@gmail.com>
---
v2:
- Check for subnormal results instead of checking floating-point exception flags.
- Add a new test case to verify that errno is correctly set to ERANGE when underflow occurs.
---
math/Makefile | 1 +
math/math-narrow.h | 2 +
math/test-narrow-sqrt-errno.c | 138 ++++++++++++++++++++++++++++++++++
3 files changed, 141 insertions(+)
create mode 100644 math/test-narrow-sqrt-errno.c
diff --git a/math/Makefile b/math/Makefile
index 57f1e4af28..da1e9b56de 100644
--- a/math/Makefile
+++ b/math/Makefile
@@ -547,6 +547,7 @@ tests = \
test-nan-overflow \
test-nan-payload \
test-narrow-macros \
+ test-narrow-sqrt-errno \
test-nearbyint-except \
test-nearbyint-except-2 \
test-powl \
diff --git a/math/math-narrow.h b/math/math-narrow.h
index 2f27b736fd..f2b9e556d8 100644
--- a/math/math-narrow.h
+++ b/math/math-narrow.h
@@ -307,6 +307,8 @@
} \
else if ((RET) == 0 && (X) != 0) \
__set_errno (ERANGE); \
+ else if ((RET) != 0 && !isnormal (RET)) \
+ __set_errno (ERANGE); \
} \
while (0)
diff --git a/math/test-narrow-sqrt-errno.c b/math/test-narrow-sqrt-errno.c
new file mode 100644
index 0000000000..bab475749b
--- /dev/null
+++ b/math/test-narrow-sqrt-errno.c
@@ -0,0 +1,138 @@
+/* Test errno setting for narrowing sqrt functions on underflow.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+ int result = 0;
+
+ /* Test fsqrt: double to float narrowing with underflow.
+ This creates a value that will underflow when sqrt is computed
+ and narrowed to float. */
+ {
+ feclearexcept (FE_ALL_EXCEPT);
+ errno = 0;
+
+ /* 1.0 / (FLT_MAX * FLT_MAX) causes underflow when sqrt and narrowed. */
+ double arg = 1.0 / (((double) FLT_MAX) * ((double) FLT_MAX));
+ float res = fsqrt (arg);
+
+ int exceptions = fetestexcept (FE_ALL_EXCEPT);
+
+ /* The result should be non-zero subnormal. */
+ TEST_VERIFY (res != 0.0f);
+ TEST_VERIFY (res < FLT_MIN);
+
+ /* Underflow exception should be raised. */
+ if ((exceptions & FE_UNDERFLOW) == 0)
+ {
+ printf ("FAIL: fsqrt: FE_UNDERFLOW not set\n");
+ result = 1;
+ }
+
+ /* errno should be set to ERANGE on underflow. */
+ if (errno != ERANGE)
+ {
+ printf ("FAIL: fsqrt: errno = %d, expected ERANGE (%d)\n",
+ errno, ERANGE);
+ result = 1;
+ }
+ }
+
+#if LDBL_MANT_DIG > DBL_MANT_DIG
+ /* Test dsqrtl: long double to double narrowing with underflow. */
+ {
+ feclearexcept (FE_ALL_EXCEPT);
+ errno = 0;
+
+ /* Create a value that will underflow when sqrt and narrowed to double. */
+ long double arg = 1.0L / (((long double) DBL_MAX) * ((long double) DBL_MAX));
+ double res = dsqrtl (arg);
+
+ int exceptions = fetestexcept (FE_ALL_EXCEPT);
+
+ /* The result should be non-zero subnormal. */
+ TEST_VERIFY (res != 0.0);
+ TEST_VERIFY (res < DBL_MIN);
+
+ /* Underflow exception should be raised. */
+ if ((exceptions & FE_UNDERFLOW) == 0)
+ {
+ printf ("FAIL: dsqrtl: FE_UNDERFLOW not set\n");
+ result = 1;
+ }
+
+ /* errno should be set to ERANGE on underflow. */
+ if (errno != ERANGE)
+ {
+ printf ("FAIL: dsqrtl: errno = %d, expected ERANGE (%d)\n",
+ errno, ERANGE);
+ result = 1;
+ }
+ }
+#endif
+
+#if LDBL_MANT_DIG > FLT_MANT_DIG
+ /* Test fsqrtl: long double to float narrowing with underflow. */
+ {
+ feclearexcept (FE_ALL_EXCEPT);
+ errno = 0;
+
+ /* Create a value that will underflow when sqrt and narrowed to float. */
+ long double arg = 1.0L / (((long double) FLT_MAX) * ((long double) FLT_MAX));
+ float res = fsqrtl (arg);
+
+ int exceptions = fetestexcept (FE_ALL_EXCEPT);
+
+ /* The result should be non-zero subnormal. */
+ TEST_VERIFY (res != 0.0f);
+ TEST_VERIFY (res < FLT_MIN);
+
+ /* Underflow exception should be raised. */
+ if ((exceptions & FE_UNDERFLOW) == 0)
+ {
+ printf ("FAIL: fsqrtl: FE_UNDERFLOW not set\n");
+ result = 1;
+ }
+
+ /* errno should be set to ERANGE on underflow. */
+ if (errno != ERANGE)
+ {
+ printf ("FAIL: fsqrtl: errno = %d, expected ERANGE (%d)\n",
+ errno, ERANGE);
+ result = 1;
+ }
+ }
+#endif
+
+ return result;
+}
+
+#include <support/test-driver.c>
+
+
+
+
--
2.43.0
More information about the Libc-alpha
mailing list