[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