[PATCH v2] math.h: Do use __MATH_TG_BUILTIN_CLASSIFY for C++
Adhemerval Zanella
adhemerval.zanella@linaro.org
Tue Dec 30 17:24:30 GMT 2025
The __MATH_TG_BUILTIN_CLASSIFY macro can not be used in C++
mode because it might use __builtin_types_compatible_p, which
is a C-only builtin.
The C++ version uses the built-in; using a different scheme,
such as function overloading (like iszero), breaks libstdc++,
which expects isinf to be implemented as a macro. The x86
test-builtin-denormal.c test has been adjusted to include the
relevant gcc/llvm bug information.
Checked with bmg targeting x86_64-linux-gnu and on
x86_64-linux-gnu and aarch64 with gcc-15 and clang-18.
---
include/array_length.h | 4 +
math/Makefile | 4 +
math/math.h | 38 ++---
math/test-math-fpclassify.cc | 149 +++++++++++++++++++
math/test-math-isinf.cc | 145 ++++++++++++++++++
scripts/sysd-rules.awk | 28 +++-
sysdeps/x86/fpu/Makefile | 5 +
sysdeps/x86/fpu/test-builtin-denormal-cxx.cc | 1 +
sysdeps/x86/fpu/test-builtin-denormal.c | 10 +-
9 files changed, 363 insertions(+), 21 deletions(-)
create mode 100644 math/test-math-fpclassify.cc
create mode 100644 math/test-math-isinf.cc
create mode 100644 sysdeps/x86/fpu/test-builtin-denormal-cxx.cc
diff --git a/include/array_length.h b/include/array_length.h
index 2a4f09fd37..2f5ab7b219 100644
--- a/include/array_length.h
+++ b/include/array_length.h
@@ -21,6 +21,9 @@
/* array_length (VAR) is the number of elements in the array VAR. VAR
must evaluate to an array, not a pointer. */
+#ifdef __cplusplus
+#define array_length(var) (sizeof (var) / sizeof ((var)[0]))
+#else
#define array_length(var) \
(sizeof (var) / sizeof ((var)[0]) \
+ 0 * sizeof (struct { \
@@ -28,6 +31,7 @@
(__typeof (var), __typeof (&(var)[0])), \
"argument must be an array"); \
}))
+#endif
/* array_end (VAR) is a pointer one past the end of the array VAR.
VAR must evaluate to an array, not a pointer. */
diff --git a/math/Makefile b/math/Makefile
index 8fe2540ce0..fa9193e75b 100644
--- a/math/Makefile
+++ b/math/Makefile
@@ -620,8 +620,10 @@ tests-static += \
ifneq (,$(CXX))
tests += \
test-math-cxx11 \
+ test-math-fpclassify \
test-math-iscanonical \
test-math-iseqsig \
+ test-math-isinf \
test-math-isinff \
test-math-issignaling \
test-math-iszero \
@@ -1151,10 +1153,12 @@ CFLAGS-test-signgam-ullong-init-static.c += -std=c99
CFLAGS-test-math-cxx11.cc += -std=c++11
+CFLAGS-test-math-isinf.cc += -std=gnu++11
CFLAGS-test-math-isinff.cc += -std=gnu++11
CFLAGS-test-math-iszero.cc += -std=gnu++11
CFLAGS-test-math-issignaling.cc += -std=gnu++11
CFLAGS-test-math-iscanonical.cc += -std=gnu++11
+CFLAGS-test-math-fpclassify.cc += -std=gnu++11
CFLAGS-test-iszero-excess-precision.c += -fexcess-precision=standard
CFLAGS-test-iseqsig-excess-precision.c += -fexcess-precision=standard
diff --git a/math/math.h b/math/math.h
index 96b64379f7..05f46b7116 100644
--- a/math/math.h
+++ b/math/math.h
@@ -1173,21 +1173,20 @@ enum
/* Return number of classification appropriate for X. */
# if ((__GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__) \
- || __glibc_clang_prereq (2,8)) \
- && (!defined __OPTIMIZE_SIZE__ || defined __cplusplus)
- /* The check for __cplusplus allows the use of the builtin, even
- when optimization for size is on. This is provided for
- libstdc++, only to let its configure test work when it is built
- with -Os. No further use of this definition of fpclassify is
- expected in C++ mode, since libstdc++ provides its own version
- of fpclassify in cmath (which undefines fpclassify). */
-# define fpclassify(x) \
- __MATH_TG_BUILTIN_CLASSIFY ((x), \
- __builtin_fpclassify, (FP_NAN, FP_INFINITE, \
- FP_NORMAL, FP_SUBNORMAL, \
- FP_ZERO, x), \
- fpclassify, (x), \
- __FP_BUILTIN_FPCLASSIFY_DENORMAL)
+ || __glibc_clang_prereq (2,8)) \
+ && !defined __cplusplus
+/* The __MATH_TG_BUILTIN_CLASSIFY macro can not be used in C++ mode because
+ it might use __builtin_types_compatible_p, which is C-only builtin. */
+# define fpclassify(x) \
+ __MATH_TG_BUILTIN_CLASSIFY ((x), \
+ __builtin_fpclassify, (FP_NAN, FP_INFINITE, \
+ FP_NORMAL, FP_SUBNORMAL, \
+ FP_ZERO, x), \
+ fpclassify, (x), \
+ __FP_BUILTIN_FPCLASSIFY_DENORMAL)
+# elif defined __cplusplus
+# define fpclassify(x) __builtin_fpclassify (FP_NAN, FP_INFINITE, FP_NORMAL, \
+ FP_SUBNORMAL, FP_ZERO, x)
# else
# define fpclassify(x) __MATH_TG ((x), __fpclassify, (x))
# endif
@@ -1247,12 +1246,17 @@ enum
# define isinf(x) \
(__builtin_types_compatible_p (__typeof (x), _Float128) \
? __isinff128 (x) : __builtin_isinf_sign (x))
-# elif (__GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__) \
- || __glibc_clang_prereq (3,7)
+ /* The __MATH_TG_BUILTIN_CLASSIFY macro can not be used in C++ mode because
+ it might use __builtin_types_compatible_p, which is C-only builtin. */
+# elif ((__GNUC_PREREQ (4,4) && !defined __SUPPORT_SNAN__) \
+ || __glibc_clang_prereq (3,7)) \
+ && !defined __cplusplus
# define isinf(x) __MATH_TG_BUILTIN_CLASSIFY ((x), \
__builtin_isinf_sign, (x), \
isinf, (x), \
__FP_BUILTIN_ISINF_SIGN_DENORMAL)
+# elif defined __cplusplus
+# define isinf(x) __builtin_isinf_sign (x)
# else
# define isinf(x) __MATH_TG ((x), __isinf, (x))
# endif
diff --git a/math/test-math-fpclassify.cc b/math/test-math-fpclassify.cc
new file mode 100644
index 0000000000..f8dbf4fe7d
--- /dev/null
+++ b/math/test-math-fpclassify.cc
@@ -0,0 +1,149 @@
+/* Test for the C++ implementation of fpclassify.
+ 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/>. */
+
+#define _GNU_SOURCE 1
+#include <math.h>
+#include <stdio.h>
+#include <limits>
+
+#include <support/check.h>
+
+/* Support for _Float128 in std::numeric_limits is limited.
+ Include ieee754_float128.h and use the bitfields in the union
+ ieee854_float128.ieee_nan to build corner-case inputs. */
+#if __HAVE_DISTINCT_FLOAT128
+# include <ieee754_float128.h>
+#endif
+
+template <class T>
+static void
+check_type ()
+{
+ typedef std::numeric_limits<T> limits;
+ TEST_COMPARE (fpclassify (T{}), FP_ZERO);
+ TEST_COMPARE (fpclassify (T{0}), FP_ZERO);
+ TEST_COMPARE (fpclassify (T{-0.0}), FP_ZERO);
+ TEST_COMPARE (fpclassify (T{1}), FP_NORMAL);
+ TEST_COMPARE (fpclassify (T{-1}), FP_NORMAL);
+ TEST_COMPARE (fpclassify (limits::min ()), FP_NORMAL);
+ TEST_COMPARE (fpclassify (-limits::min ()), FP_NORMAL);
+ TEST_COMPARE (fpclassify (limits::max ()), FP_NORMAL);
+ TEST_COMPARE (fpclassify (-limits::max ()), FP_NORMAL);
+ if (limits::has_infinity)
+ {
+ TEST_COMPARE (fpclassify (limits::infinity ()), FP_INFINITE);
+ TEST_COMPARE (fpclassify (-limits::infinity ()), FP_INFINITE);
+ }
+ TEST_COMPARE (fpclassify (limits::epsilon ()), FP_NORMAL);
+ TEST_COMPARE (fpclassify (-limits::epsilon ()), FP_NORMAL);
+ if (limits::has_quiet_NaN)
+ TEST_COMPARE (fpclassify (limits::quiet_NaN ()), FP_NAN);
+ if (limits::has_signaling_NaN)
+ TEST_COMPARE (fpclassify (limits::signaling_NaN ()), FP_NAN);
+ if (limits::has_signaling_NaN)
+ TEST_COMPARE (fpclassify (limits::signaling_NaN ()), FP_NAN);
+ TEST_COMPARE (fpclassify (limits::denorm_min ()),
+ std::numeric_limits<T>::has_denorm == std::denorm_absent
+ ? FP_NORMAL : FP_SUBNORMAL);
+ TEST_COMPARE (fpclassify (-limits::denorm_min ()),
+ std::numeric_limits<T>::has_denorm == std::denorm_absent
+ ? FP_NORMAL : FP_SUBNORMAL);
+}
+
+#if __HAVE_DISTINCT_FLOAT128
+static void
+check_float128 ()
+{
+ ieee854_float128 q;
+
+ q.d = 0.0Q;
+ TEST_COMPARE (fpclassify (q.d), FP_ZERO);
+ q.d = -0.0Q;
+ TEST_COMPARE (fpclassify (q.d), FP_ZERO);
+ q.d = 1.0Q;
+ TEST_COMPARE (fpclassify (q.d), FP_NORMAL);
+ q.d = -1.0Q;
+ TEST_COMPARE (fpclassify (q.d), FP_NORMAL);
+
+ /* Normal min. */
+ q.ieee.negative = 0;
+ q.ieee.exponent = 0x0001;
+ q.ieee.mantissa0 = 0x0000;
+ q.ieee.mantissa1 = 0x00000000;
+ q.ieee.mantissa2 = 0x00000000;
+ q.ieee.mantissa3 = 0x00000000;
+ TEST_COMPARE (fpclassify (q.d), FP_NORMAL);
+ q.ieee.negative = 1;
+ TEST_COMPARE (fpclassify (q.d), FP_NORMAL);
+
+ /* Normal max. */
+ q.ieee.negative = 0;
+ q.ieee.exponent = 0x7FFE;
+ q.ieee.mantissa0 = 0xFFFF;
+ q.ieee.mantissa1 = 0xFFFFFFFF;
+ q.ieee.mantissa2 = 0xFFFFFFFF;
+ q.ieee.mantissa3 = 0xFFFFFFFF;
+ TEST_COMPARE (fpclassify (q.d), FP_NORMAL);
+ q.ieee.negative = 1;
+ TEST_COMPARE (fpclassify (q.d), FP_NORMAL);
+
+ /* Infinity. */
+ q.ieee.negative = 0;
+ q.ieee.exponent = 0x7FFF;
+ q.ieee.mantissa0 = 0x0000;
+ q.ieee.mantissa1 = 0x00000000;
+ q.ieee.mantissa2 = 0x00000000;
+ q.ieee.mantissa3 = 0x00000000;
+ TEST_COMPARE (fpclassify (q.d), FP_INFINITE);
+
+ /* Quiet NaN. */
+ q.ieee_nan.quiet_nan = 1;
+ q.ieee_nan.mantissa0 = 0x0000;
+ TEST_COMPARE (fpclassify (q.d), FP_NAN);
+
+ /* Signaling NaN. */
+ q.ieee_nan.quiet_nan = 0;
+ q.ieee_nan.mantissa0 = 0x4000;
+ TEST_COMPARE (fpclassify (q.d), FP_NAN);
+
+ /* Denormal min. */
+ q.ieee.negative = 0;
+ q.ieee.exponent = 0x0000;
+ q.ieee.mantissa0 = 0x0000;
+ q.ieee.mantissa1 = 0x00000000;
+ q.ieee.mantissa2 = 0x00000000;
+ q.ieee.mantissa3 = 0x00000001;
+ TEST_COMPARE (fpclassify (q.d), FP_SUBNORMAL);
+ q.ieee.negative = 1;
+ TEST_COMPARE (fpclassify (q.d), FP_SUBNORMAL);
+}
+#endif
+
+static int
+do_test (void)
+{
+ check_type<float> ();
+ check_type<double> ();
+ check_type<long double> ();
+#if __HAVE_DISTINCT_FLOAT128
+ check_float128 ();
+#endif
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/math/test-math-isinf.cc b/math/test-math-isinf.cc
new file mode 100644
index 0000000000..4cdc146583
--- /dev/null
+++ b/math/test-math-isinf.cc
@@ -0,0 +1,145 @@
+/* Test for the C++ implementation of isinf.
+ 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/>. */
+
+#define _GNU_SOURCE 1
+#include <math.h>
+#include <stdio.h>
+#include <limits>
+
+#include <support/check.h>
+
+/* Support for _Float128 in std::numeric_limits is limited.
+ Include ieee754_float128.h and use the bitfields in the union
+ ieee854_float128.ieee_nan to build corner-case inputs. */
+#if __HAVE_DISTINCT_FLOAT128
+# include <ieee754_float128.h>
+#endif
+
+template <class T>
+static void
+check_type ()
+{
+ typedef std::numeric_limits<T> limits;
+ TEST_COMPARE (isinf (T{}), 0);
+ TEST_COMPARE (isinf (T{0}), 0);
+ TEST_COMPARE (isinf (T{-0.0}), 0);
+ TEST_COMPARE (isinf (T{1}), 0);
+ TEST_COMPARE (isinf (T{-1}), 0);
+ TEST_COMPARE (isinf (limits::min ()), 0);
+ TEST_COMPARE (isinf (-limits::min ()), 0);
+ TEST_COMPARE (isinf (limits::max ()), 0);
+ TEST_COMPARE (isinf (-limits::max ()), 0);
+ if (limits::has_infinity)
+ {
+ TEST_COMPARE (isinf (limits::infinity ()), 1);
+ TEST_COMPARE (isinf (-limits::infinity ()), -1);
+ }
+ TEST_COMPARE (isinf (limits::epsilon ()), 0);
+ TEST_COMPARE (isinf (-limits::epsilon ()), 0);
+ if (limits::has_quiet_NaN)
+ TEST_COMPARE (isinf (limits::quiet_NaN ()), 0);
+ if (limits::has_signaling_NaN)
+ TEST_COMPARE (isinf (limits::signaling_NaN ()), 0);
+ if (limits::has_signaling_NaN)
+ TEST_COMPARE (isinf (limits::signaling_NaN ()), 0);
+ TEST_COMPARE (isinf (limits::denorm_min ()), 0);
+ TEST_COMPARE (isinf (-limits::denorm_min ()), 0);
+}
+
+#if __HAVE_DISTINCT_FLOAT128
+static void
+check_float128 ()
+{
+ ieee854_float128 q;
+
+ q.d = 0.0Q;
+ TEST_COMPARE (isinf (q.d), 0);
+ q.d = -0.0Q;
+ TEST_COMPARE (isinf (q.d), 0);
+ q.d = 1.0Q;
+ TEST_COMPARE (isinf (q.d), 0);
+ q.d = -1.0Q;
+ TEST_COMPARE (isinf (q.d), 0);
+
+ /* Normal min. */
+ q.ieee.negative = 0;
+ q.ieee.exponent = 0x0001;
+ q.ieee.mantissa0 = 0x0000;
+ q.ieee.mantissa1 = 0x00000000;
+ q.ieee.mantissa2 = 0x00000000;
+ q.ieee.mantissa3 = 0x00000000;
+ TEST_COMPARE (isinf (q.d), 0);
+ q.ieee.negative = 1;
+ TEST_COMPARE (isinf (q.d), 0);
+
+ /* Normal max. */
+ q.ieee.negative = 0;
+ q.ieee.exponent = 0x7FFE;
+ q.ieee.mantissa0 = 0xFFFF;
+ q.ieee.mantissa1 = 0xFFFFFFFF;
+ q.ieee.mantissa2 = 0xFFFFFFFF;
+ q.ieee.mantissa3 = 0xFFFFFFFF;
+ TEST_COMPARE (isinf (q.d), 0);
+ q.ieee.negative = 1;
+ TEST_COMPARE (isinf (q.d), 0);
+
+ /* Infinity. */
+ q.ieee.negative = 0;
+ q.ieee.exponent = 0x7FFF;
+ q.ieee.mantissa0 = 0x0000;
+ q.ieee.mantissa1 = 0x00000000;
+ q.ieee.mantissa2 = 0x00000000;
+ q.ieee.mantissa3 = 0x00000000;
+ TEST_COMPARE (isinf (q.d), 1);
+
+ /* Quiet NaN. */
+ q.ieee_nan.quiet_nan = 1;
+ q.ieee_nan.mantissa0 = 0x0000;
+ TEST_COMPARE (isinf (q.d), 0);
+
+ /* Signaling NaN. */
+ q.ieee_nan.quiet_nan = 0;
+ q.ieee_nan.mantissa0 = 0x4000;
+ TEST_COMPARE (isinf (q.d), 0);
+
+ /* Denormal min. */
+ q.ieee.negative = 0;
+ q.ieee.exponent = 0x0000;
+ q.ieee.mantissa0 = 0x0000;
+ q.ieee.mantissa1 = 0x00000000;
+ q.ieee.mantissa2 = 0x00000000;
+ q.ieee.mantissa3 = 0x00000001;
+ TEST_COMPARE (isinf (q.d), 0);
+ q.ieee.negative = 1;
+ TEST_COMPARE (isinf (q.d), 0);
+}
+#endif
+
+static int
+do_test (void)
+{
+ check_type<float> ();
+ check_type<double> ();
+ check_type<long double> ();
+#if __HAVE_DISTINCT_FLOAT128
+ check_float128 ();
+#endif
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/scripts/sysd-rules.awk b/scripts/sysd-rules.awk
index c82e8fd607..cdc892b397 100644
--- a/scripts/sysd-rules.awk
+++ b/scripts/sysd-rules.awk
@@ -35,6 +35,7 @@ BEGIN {
for (sysdir_idx = 1; sysdir_idx <= nsysdirs; ++sysdir_idx) {
dir = sysdirs[sysdir_idx];
+ sysdep_dir = dir
if (dir !~ /^\//) dir = "$(..)" dir;
asm_rules = 1;
for (i = 1; i <= ninhibit_asm; ++i) {
@@ -43,6 +44,20 @@ BEGIN {
break;
}
}
+ # Check if there are any *.S files.
+ if (asm_rules == 1 && system("ls " sysdep_dir "/*.S > /dev/null 2>&1") != 0) {
+ asm_rules = 0;
+ }
+ # Check if there are any *.c files.
+ c_rules = 1
+ if (system("ls " sysdep_dir "/*.c > /dev/null 2>&1") != 0) {
+ c_rules = 0;
+ }
+ # Check if there are any *.cc files.
+ cc_rules = 1
+ if (system("ls " sysdep_dir "/*.cc > /dev/null 2>&1") != 0) {
+ cc_rules = 0;
+ }
for (suffix_idx = 1; suffix_idx <= nsuffixes; ++suffix_idx) {
o = suffixes[suffix_idx];
for (pattern_idx = 1; pattern_idx <= npatterns; ++pattern_idx) {
@@ -66,9 +81,16 @@ BEGIN {
print target, dep, "$(before-compile)";
print "\t$(compile-command.S)" command_suffix;
}
- dep = dir "/" dep_pattern ".c";
- print target, dep, "$(before-compile)";
- print "\t$(compile-command.c)" command_suffix;
+ if (c_rules) {
+ dep = dir "/" dep_pattern ".c";
+ print target, dep, "$(before-compile)";
+ print "\t$(compile-command.c)" command_suffix;
+ }
+ if (cc_rules) {
+ dep = dir "/" dep_pattern ".cc";
+ print target, dep, "$(before-compile)";
+ print "\t$(compile-command.cc)" command_suffix;
+ }
}
}
print "$(inst_includedir)/%.h:", dir "/%.h", "$(+force)";
diff --git a/sysdeps/x86/fpu/Makefile b/sysdeps/x86/fpu/Makefile
index 8566d3c80f..cb0879dd5f 100644
--- a/sysdeps/x86/fpu/Makefile
+++ b/sysdeps/x86/fpu/Makefile
@@ -11,6 +11,11 @@ tests += \
test-fenv-x87 \
test-flt-eval-method-sse \
# tests
+ifneq (,$(CXX))
+tests += \
+ test-builtin-denormal-cxx \
+# tests
+endif
ifeq ($(have-test-cc-cflags-mfpmath-387),yes)
tests += test-flt-eval-method-387
endif
diff --git a/sysdeps/x86/fpu/test-builtin-denormal-cxx.cc b/sysdeps/x86/fpu/test-builtin-denormal-cxx.cc
new file mode 100644
index 0000000000..0b7e8e8728
--- /dev/null
+++ b/sysdeps/x86/fpu/test-builtin-denormal-cxx.cc
@@ -0,0 +1 @@
+#include "test-builtin-denormal.c"
diff --git a/sysdeps/x86/fpu/test-builtin-denormal.c b/sysdeps/x86/fpu/test-builtin-denormal.c
index bddcd59387..789f1e3535 100644
--- a/sysdeps/x86/fpu/test-builtin-denormal.c
+++ b/sysdeps/x86/fpu/test-builtin-denormal.c
@@ -40,15 +40,23 @@ static const ieee_long_double_shape_type inputs[] = {
static int
do_test (void)
{
- for (int i = 0; i < array_length (inputs); i++)
+ for (unsigned int i = 0; i < array_length (inputs); i++)
{
TEST_COMPARE (feclearexcept (FE_INVALID), 0);
TEST_COMPARE (fpclassify (inputs[i].value), FP_NAN);
+#ifndef __cplusplus
+ /* C++ uses the builtin, which does not trigger FE_INVALID for
+ pseudo-numbers (GCC BZ 123161 and LLVM issue 172533. */
TEST_COMPARE (fetestexcept (FE_INVALID), 0);
+#endif
TEST_COMPARE (feclearexcept (FE_INVALID), 0);
TEST_COMPARE (isinf (inputs[i].value), 0);
+#ifndef __cplusplus
+ /* C++ uses the builtin, which does not trigger FE_INVALID for
+ pseudo-numbers (GCC BZ 123173 and LLVM issue 172651. */
TEST_COMPARE (fetestexcept (FE_INVALID), 0);
+#endif
}
return 0;
--
2.43.0
More information about the Libc-alpha
mailing list