Page MenuHomeFreeBSD

D60642.id.diff
No OneTemporary

D60642.id.diff

diff --git a/lib/libc/gen/isgreater.3 b/lib/libc/gen/isgreater.3
--- a/lib/libc/gen/isgreater.3
+++ b/lib/libc/gen/isgreater.3
@@ -82,6 +82,7 @@
of the relationships (less, greater, equal, unordered) holds.
.Sh SEE ALSO
.Xr fpclassify 3 ,
+.Xr iseqsig 3 ,
.Xr math 3 ,
.Xr signbit 3
.Sh STANDARDS
diff --git a/lib/msun/Makefile b/lib/msun/Makefile
--- a/lib/msun/Makefile
+++ b/lib/msun/Makefile
@@ -88,7 +88,8 @@
s_fminimum_mag.c s_fminimum_magf.c s_fminimum_mag_num.c \
s_fminimum_mag_numf.c s_fminimum_num.c s_fminimum_numf.c \
s_frexp.c s_frexpf.c s_ilogb.c s_ilogbf.c \
- s_ilogbl.c s_isfinite.c s_isnan.c s_isnormal.c \
+ s_ilogbl.c s_iseqsig.c s_iseqsigf.c s_isfinite.c s_isnan.c \
+ s_isnormal.c \
s_llrint.c s_llrintf.c s_llround.c s_llroundf.c s_llroundl.c \
s_log1p.c s_log1pf.c s_logb.c s_logbf.c s_lrint.c s_lrintf.c \
s_lround.c s_lroundf.c s_lroundl.c s_modff.c \
@@ -143,7 +144,7 @@
s_fmaximum_numl.c s_fmaximum_mag_numl.c \
s_fminl.c s_fminimuml.c s_fminimum_magl.c \
s_fminimum_numl.c s_fminimum_mag_numl.c \
- s_frexpl.c s_logbl.c s_logl.c s_nanl.c \
+ s_frexpl.c s_iseqsigl.c s_logbl.c s_logl.c s_nanl.c \
s_nextafterl.c s_nexttoward.c s_remquol.c s_rintl.c s_roundl.c \
s_scalbnl.c s_sinl.c s_sincosl.c s_sinpil.c s_rsqrtl.c \
s_tanhl.c s_tanl.c s_tanpil.c s_truncl.c w_cabsl.c
@@ -191,7 +192,7 @@
fegetmode.3 fegetround.3 fenv.3 floor.3 fma.3 \
fmax.3 fmaximum.3 fmaximum_mag.3 \
fmaximum_mag_num.3 fmaximum_num.3 fmod.3 \
- hypot.3 ieee.3 ieee_test.3 ilogb.3 j0.3 \
+ hypot.3 ieee.3 ieee_test.3 ilogb.3 iseqsig.3 j0.3 \
lgamma.3 log.3 lrint.3 lround.3 math.3 nan.3 \
nextafter.3 remainder.3 rint.3 \
round.3 scalbn.3 signbit.3 sin.3 sincos.3 \
diff --git a/lib/msun/Symbol.map b/lib/msun/Symbol.map
--- a/lib/msun/Symbol.map
+++ b/lib/msun/Symbol.map
@@ -356,6 +356,9 @@
fminimum_mag_num;
fminimum_mag_numf;
fminimum_mag_numl;
+ __iseqsig;
+ __iseqsigf;
+ __iseqsigl;
rsqrt;
rsqrtf;
rsqrtl;
diff --git a/lib/msun/man/iseqsig.3 b/lib/msun/man/iseqsig.3
new file mode 100644
--- /dev/null
+++ b/lib/msun/man/iseqsig.3
@@ -0,0 +1,56 @@
+.\" Copyright (c) 2026 Faraz Vahedi <kfv@FreeBSD.org>
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd October 12, 2026
+.Dt ISEQSIG 3
+.Os
+.Sh NAME
+.Nm iseqsig
+.Nd "signaling floating-point equality comparison"
+.Sh LIBRARY
+.Lb libm
+.Sh SYNOPSIS
+.In math.h
+.Ft int
+.Fn iseqsig "real-floating x" "real-floating y"
+.Sh DESCRIPTION
+The
+.Fn iseqsig
+macro determines whether its arguments are equal.
+Arguments of different types are compared as by the
+.Li ==
+operator, after the usual arithmetic conversions.
+Positive and negative zero compare equal.
+.Pp
+Unlike the equality operator, which is a quiet comparison,
+.Fn iseqsig
+raises the
+.Dv FE_INVALID
+floating-point exception if either argument is a NaN, quiet or
+signaling, considered a domain error.
+As
+.Va math_errhandling
+is
+.Dv MATH_ERREXCEPT ,
+.Va errno
+is not set.
+.Sh RETURN VALUES
+The
+.Fn iseqsig
+macro returns 1 if its arguments are equal and 0 otherwise.
+If either argument is a NaN, it returns 0.
+.Sh SEE ALSO
+.Xr fenv 3 ,
+.Xr isgreater 3 ,
+.Xr math 3
+.Sh STANDARDS
+The
+.Fn iseqsig
+macro conforms to
+.St -isoC-2023 .
+.Sh HISTORY
+The
+.Fn iseqsig
+macro first appeared in
+.Fx 16.0 .
diff --git a/lib/msun/man/math.3 b/lib/msun/man/math.3
--- a/lib/msun/man/math.3
+++ b/lib/msun/man/math.3
@@ -178,6 +178,10 @@
islessgreater less than or greater than relation
isunordered unordered relation
.El
+.Ss Signaling Order Predicates
+.Cl
+iseqsig equal to relation, signaling on NaN
+.El
.Ss Transcendental Functions
.Cl
acos inverse cosine
diff --git a/lib/msun/src/math.h b/lib/msun/src/math.h
--- a/lib/msun/src/math.h
+++ b/lib/msun/src/math.h
@@ -114,6 +114,37 @@
#define isunordered(x, y) (isnan(x) || isnan(y))
#endif /* __MATH_BUILTIN_RELOPS */
+#if __ISO_C_VISIBLE >= 2023
+/*
+ * Dispatch on the common type of the arguments widened to float_t, so that
+ * an argument evaluated in a format wider than its semantic type is not
+ * narrowed before it is compared (C23 §F.10.14.1). The selector is never
+ * evaluated.
+ */
+#if __STDC_VERSION__ >= 201112L || __has_extension(c_generic_selections)
+#define iseqsig(x, y) __extension__ _Generic((x) + (y) + (__float_t)0, \
+ float: __iseqsigf, \
+ double: __iseqsig, \
+ long double: __iseqsigl)((x), (y))
+#elif !defined(__cplusplus)
+#define iseqsig(x, y) __builtin_choose_expr( \
+ __builtin_types_compatible_p(__typeof((x) + (y) + (__float_t)0), \
+ long double), __iseqsigl((x), (y)), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(__typeof((x) + (y) + (__float_t)0), \
+ double), __iseqsig((x), (y)), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(__typeof((x) + (y) + (__float_t)0), \
+ float), __iseqsigf((x), (y)), (void)0)))
+#else
+#define iseqsig(x, y) \
+ ((sizeof((x) + (y) + (__float_t)0) == sizeof(float)) ? \
+ __iseqsigf((x), (y)) : \
+ (sizeof((x) + (y) + (__float_t)0) == sizeof(double)) ? \
+ __iseqsig((x), (y)) : __iseqsigl((x), (y)))
+#endif
+#endif /* __ISO_C_VISIBLE >= 2023 */
+
#define signbit(x) __fp_type_select(x, __signbitf, __signbit, __signbitl)
typedef __double_t double_t;
@@ -556,6 +587,11 @@
double rsqrt(double);
float rsqrtf(float);
long double rsqrtl(long double);
+
+/* Not __pure2: these raise "invalid" on a NaN argument. */
+int __iseqsig(double, double);
+int __iseqsigf(float, float);
+int __iseqsigl(long double, long double);
#endif /* __ISO_C_VISIBLE >= 2023 */
__END_DECLS
diff --git a/lib/msun/src/s_iseqsig.c b/lib/msun/src/s_iseqsig.c
new file mode 100644
--- /dev/null
+++ b/lib/msun/src/s_iseqsig.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2026 Faraz Vahedi <kfv@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+
+int
+__iseqsig(double x, double y)
+{
+ if (isunordered(x, y)) {
+ feraiseexcept(FE_INVALID);
+ return (0);
+ }
+ return (x == y);
+}
+
+#if (LDBL_MANT_DIG == 53)
+__weak_reference(__iseqsig, __iseqsigl);
+#endif
diff --git a/lib/msun/src/s_iseqsigf.c b/lib/msun/src/s_iseqsigf.c
new file mode 100644
--- /dev/null
+++ b/lib/msun/src/s_iseqsigf.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2026 Faraz Vahedi <kfv@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <fenv.h>
+#include <math.h>
+
+int
+__iseqsigf(float x, float y)
+{
+ if (isunordered(x, y)) {
+ feraiseexcept(FE_INVALID);
+ return (0);
+ }
+ return (x == y);
+}
diff --git a/lib/msun/src/s_iseqsigl.c b/lib/msun/src/s_iseqsigl.c
new file mode 100644
--- /dev/null
+++ b/lib/msun/src/s_iseqsigl.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2026 Faraz Vahedi <kfv@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <fenv.h>
+#include <math.h>
+
+int
+__iseqsigl(long double x, long double y)
+{
+ if (isunordered(x, y)) {
+ feraiseexcept(FE_INVALID);
+ return (0);
+ }
+ return (x == y);
+}
diff --git a/lib/msun/tests/Makefile b/lib/msun/tests/Makefile
--- a/lib/msun/tests/Makefile
+++ b/lib/msun/tests/Makefile
@@ -63,6 +63,7 @@
ATF_TESTS_C+= ilogb2_test
ATF_TESTS_C+= invtrig_test
ATF_TESTS_C+= invctrig_test
+ATF_TESTS_C+= iseqsig_test
ATF_TESTS_C+= logarithm_test
ATF_TESTS_C+= lrint_test
ATF_TESTS_C+= lround_test
diff --git a/lib/msun/tests/iseqsig_test.c b/lib/msun/tests/iseqsig_test.c
new file mode 100644
--- /dev/null
+++ b/lib/msun/tests/iseqsig_test.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2026 Faraz Vahedi <kfv@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/cdefs.h>
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+
+#include "test-utils.h"
+
+#pragma STDC FENV_ACCESS ON
+
+/*
+ * Test that iseqsig(x, y) returns exactly `expected' and raises exactly
+ * `excepts' out of the standard exceptions.
+ */
+#define TEST(tx, x, ty, y, expected, excepts) do { \
+ volatile tx __x = (x); \
+ volatile ty __y = (y); \
+ int __r; \
+ ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT)); \
+ __r = iseqsig(__x, __y); \
+ CHECK_FP_EXCEPTIONS_MSG((excepts), ALL_STD_EXCEPT, \
+ "iseqsig((" #tx ")%La, (" #ty ")%La)", \
+ (long double)__x, (long double)__y); \
+ ATF_CHECK_EQ_MSG((expected), __r, \
+ "iseqsig((" #tx ")%La, (" #ty ")%La) = %d, expected %d", \
+ (long double)__x, (long double)__y, __r, (expected)); \
+} while (0)
+
+/* Test x and y in both orders, in each of the three types. */
+#define TEST_ALL(x, y, expected, excepts) do { \
+ TEST(float, x, float, y, expected, excepts); \
+ TEST(float, y, float, x, expected, excepts); \
+ TEST(double, x, double, y, expected, excepts); \
+ TEST(double, y, double, x, expected, excepts); \
+ TEST(long double, x, long double, y, expected, excepts); \
+ TEST(long double, y, long double, x, expected, excepts); \
+} while (0)
+
+/* Edge cases that depend on the format, in both orders. */
+#define TEST_FORMAT(t, max, true_min, next1) do { \
+ TEST(t, max, t, max, 1, 0); \
+ TEST(t, -max, t, -max, 1, 0); \
+ TEST(t, max, t, INFINITY, 0, 0); \
+ TEST(t, INFINITY, t, max, 0, 0); \
+ TEST(t, true_min, t, true_min, 1, 0); \
+ TEST(t, true_min, t, 0.0, 0, 0); \
+ TEST(t, 0.0, t, true_min, 0, 0); \
+ TEST(t, -true_min, t, -0.0, 0, 0); \
+ TEST(t, true_min, t, -true_min, 0, 0); \
+ TEST(t, 1.0, t, next1, 0, 0); \
+ TEST(t, next1, t, 1.0, 0, 0); \
+ TEST(t, next1, t, next1, 1, 0); \
+} while (0)
+
+ATF_TC_WITHOUT_HEAD(equal);
+ATF_TC_BODY(equal, tc)
+{
+ TEST_ALL(0.0, 0.0, 1, 0);
+ TEST_ALL(0.0, -0.0, 1, 0);
+ TEST_ALL(-0.0, -0.0, 1, 0);
+ TEST_ALL(1.0, 1.0, 1, 0);
+ TEST_ALL(-0.5, -0.5, 1, 0);
+ TEST_ALL(0x1.fffffep+100, 0x1.fffffep+100, 1, 0);
+ TEST_ALL(INFINITY, INFINITY, 1, 0);
+ TEST_ALL(-INFINITY, -INFINITY, 1, 0);
+}
+
+ATF_TC_WITHOUT_HEAD(unequal);
+ATF_TC_BODY(unequal, tc)
+{
+ TEST_ALL(0.0, 1.0, 0, 0);
+ TEST_ALL(1.0, -1.0, 0, 0);
+ TEST_ALL(1.0, 2.0, 0, 0);
+ TEST_ALL(0.0, INFINITY, 0, 0);
+ TEST_ALL(-0.0, -INFINITY, 0, 0);
+ TEST_ALL(INFINITY, -INFINITY, 0, 0);
+}
+
+ATF_TC_WITHOUT_HEAD(format_edges);
+ATF_TC_BODY(format_edges, tc)
+{
+ TEST_FORMAT(float, FLT_MAX, FLT_TRUE_MIN, nextafterf(1, 2));
+ TEST_FORMAT(double, DBL_MAX, DBL_TRUE_MIN, nextafter(1, 2));
+ TEST_FORMAT(long double, LDBL_MAX, LDBL_TRUE_MIN, nextafterl(1, 2));
+}
+
+/*
+ * A quiet NaN in either position, or both, is a domain error: the result
+ * is 0 and "invalid" is raised, and nothing else is.
+ */
+ATF_TC_WITHOUT_HEAD(quiet_nan);
+ATF_TC_BODY(quiet_nan, tc)
+{
+ TEST_ALL(NAN, 0.0, 0, FE_INVALID);
+ TEST_ALL(NAN, -0.0, 0, FE_INVALID);
+ TEST_ALL(NAN, 1.0, 0, FE_INVALID);
+ TEST_ALL(NAN, INFINITY, 0, FE_INVALID);
+ TEST_ALL(NAN, -INFINITY, 0, FE_INVALID);
+ TEST_ALL(NAN, NAN, 0, FE_INVALID);
+ TEST_ALL(-NAN, 1.0, 0, FE_INVALID);
+ TEST_ALL(-NAN, NAN, 0, FE_INVALID);
+}
+
+ATF_TC_WITHOUT_HEAD(signaling_nan);
+ATF_TC_BODY(signaling_nan, tc)
+{
+ TEST(float, __builtin_nansf(""), float, 1.0f, 0, FE_INVALID);
+ TEST(float, 1.0f, float, __builtin_nansf(""), 0, FE_INVALID);
+ TEST(float, __builtin_nansf(""), float, __builtin_nansf(""), 0,
+ FE_INVALID);
+ TEST(double, __builtin_nans(""), double, 1.0, 0, FE_INVALID);
+ TEST(double, 1.0, double, __builtin_nans(""), 0, FE_INVALID);
+ TEST(double, __builtin_nans(""), double, __builtin_nans(""), 0,
+ FE_INVALID);
+ TEST(long double, __builtin_nansl(""), long double, 1.0L, 0,
+ FE_INVALID);
+ TEST(long double, 1.0L, long double, __builtin_nansl(""), 0,
+ FE_INVALID);
+ TEST(long double, __builtin_nansl(""), long double,
+ __builtin_nansl(""), 0, FE_INVALID);
+}
+
+/*
+ * Arguments of different types are compared as == would compare them,
+ * after the usual arithmetic conversions.
+ */
+ATF_TC_WITHOUT_HEAD(mixed_types);
+ATF_TC_BODY(mixed_types, tc)
+{
+ TEST(float, 0.5f, double, 0.5, 1, 0);
+ TEST(double, 0.5, float, 0.5f, 1, 0);
+ TEST(float, 0.5f, long double, 0.5L, 1, 0);
+ TEST(double, 0.5, long double, 0.5L, 1, 0);
+ TEST(long double, 0.5L, double, 0.5, 1, 0);
+
+ /* 0.1 is not representable; its roundings differ per format. */
+ TEST(float, 0.1f, double, 0.1, 0, 0);
+ TEST(double, 0.1, float, 0.1f, 0, 0);
+ TEST(float, 0.1f, long double, 0.1L, 0, 0);
+ TEST(double, 0.1, long double, 0.1L,
+ LDBL_MANT_DIG == DBL_MANT_DIG, 0);
+ TEST(long double, 0.1L, double, 0.1,
+ LDBL_MANT_DIG == DBL_MANT_DIG, 0);
+
+ /* The wider argument must not be narrowed to the narrower type. */
+ TEST(float, 1.0f, double, nextafter(1, 2), 0, 0);
+ TEST(double, 1.0, long double, nextafterl(1, 2), 0, 0);
+
+ TEST(float, NAN, double, 1.0, 0, FE_INVALID);
+ TEST(double, 1.0, long double, NAN, 0, FE_INVALID);
+ TEST(float, 1.0f, long double, NAN, 0, FE_INVALID);
+}
+
+/* iseqsig() is a macro; each argument must be evaluated exactly once. */
+ATF_TC_WITHOUT_HEAD(single_evaluation);
+ATF_TC_BODY(single_evaluation, tc)
+{
+ double a[] = { 1.0, 1.0, 2.0 };
+ float b[] = { 1.0f, 2.0f };
+ int i, j, r;
+
+ i = j = 0;
+ r = iseqsig(a[i++], a[++j]);
+ ATF_CHECK_EQ(1, r);
+ ATF_CHECK_EQ(1, i);
+ ATF_CHECK_EQ(1, j);
+
+ i = j = 0;
+ r = iseqsig(b[i++], a[j += 2]);
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_EQ(1, i);
+ ATF_CHECK_EQ(2, j);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, equal);
+ ATF_TP_ADD_TC(tp, unequal);
+ ATF_TP_ADD_TC(tp, format_edges);
+ ATF_TP_ADD_TC(tp, quiet_nan);
+ ATF_TP_ADD_TC(tp, signaling_nan);
+ ATF_TP_ADD_TC(tp, mixed_types);
+ ATF_TP_ADD_TC(tp, single_evaluation);
+
+ return (atf_no_error());
+}

File Metadata

Mime Type
text/plain
Expires
Tue, Oct 13, 11:12 PM (12 h, 47 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40755556
Default Alt Text
D60642.id.diff (13 KB)

Event Timeline