add missing FreeBSD libm tests, update the others
authormbuhl <mbuhl@openbsd.org>
Fri, 22 Oct 2021 18:00:22 +0000 (18:00 +0000)
committermbuhl <mbuhl@openbsd.org>
Fri, 22 Oct 2021 18:00:22 +0000 (18:00 +0000)
25 files changed:
regress/lib/libm/msun/Makefile
regress/lib/libm/msun/atf-c.c [new file with mode: 0644]
regress/lib/libm/msun/atf-c.h [new file with mode: 0644]
regress/lib/libm/msun/cexp_test.c [new file with mode: 0644]
regress/lib/libm/msun/conj_test.c
regress/lib/libm/msun/csqrt_test.c [new file with mode: 0644]
regress/lib/libm/msun/ctrig_test.c [new file with mode: 0644]
regress/lib/libm/msun/exponential_test.c [new file with mode: 0644]
regress/lib/libm/msun/fenv_test.c
regress/lib/libm/msun/fls.c [new file with mode: 0644]
regress/lib/libm/msun/fma_test.c [new file with mode: 0644]
regress/lib/libm/msun/fmaxmin_test.c [new file with mode: 0644]
regress/lib/libm/msun/ilogb_test.c
regress/lib/libm/msun/invctrig_test.c [new file with mode: 0644]
regress/lib/libm/msun/invtrig_test.c [new file with mode: 0644]
regress/lib/libm/msun/logarithm_test.c [new file with mode: 0644]
regress/lib/libm/msun/lrint_test.c
regress/lib/libm/msun/lround_test.c [new file with mode: 0644]
regress/lib/libm/msun/macros.h [new file with mode: 0644]
regress/lib/libm/msun/nan_test.c [new file with mode: 0644]
regress/lib/libm/msun/nearbyint_test.c [new file with mode: 0644]
regress/lib/libm/msun/next_test.c [new file with mode: 0644]
regress/lib/libm/msun/rem_test.c [new file with mode: 0644]
regress/lib/libm/msun/test-utils.h
regress/lib/libm/msun/trig_test.c [new file with mode: 0644]

index 16f9169..1894ef6 100644 (file)
-# $OpenBSD: Makefile,v 1.3 2020/12/17 00:51:11 bluhm Exp $
+#      $OpenBSD: Makefile,v 1.4 2021/10/22 18:00:22 mbuhl Exp $
+
+# Copyright (c) 2021 Moritz Buhl <mbuhl@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# Each test program in PROGS may define several numbered subtests.
+# In a first step compile all programs and extract their parameters.
+# For each PROG define new regression subtests based on the test number.
+
+.if defined(NUMBERS)
+REGRESS_TARGETS =      ${NUMBERS:C/(.*)/run-${PROG}-\1 cleanup-${PROG}-\1/}
+.else
+REGRESS_TARGETS =      ${PROGS:S/^/run-/}
+.endif
+
+PROGS =
+PROGS+= cexp_test
+PROGS+= conj_test
+PROGS+= csqrt_test
+PROGS+= ctrig_test
+PROGS+= exponential_test
+PROGS+= fenv_test
+PROGS+= fma_test
+PROGS+= fmaxmin_test
+PROGS+= ilogb_test
+PROGS+= invctrig_test
+PROGS+= invtrig_test
+PROGS+= logarithm_test
+PROGS+= lrint_test
+PROGS+= lround_test
+PROGS+= nan_test
+PROGS+= nearbyint_test
+PROGS+= next_test
+PROGS+= rem_test
+PROGS+= trig_test
+
+# failing tests
+FAILING = run-csqrt_test-{1,2,3} run-ctrig_test-{2,3,4,6} \
+       run-exponential_test-1 run-fma_test-7 run-invctrig_test-{1,2,3,4} \
+       run-invtrig_test-{1,2,3,5,6} run-logarithm_test-1 run-lround_test-1 \
+       run-nan_test-1 run-nearbyint_test-1 run-next_test-5 run-rem_test-3
+
+. if ${MACHINE} == amd64
+FAILING+=      run-cexp_test-{1,7}
+FAILING+=      run-ctrig_test-1
+FAILING+=      run-invtrig_test-7
+FAILING+=      run-next_test-{1,2,4}
+FAILING+=      run-trig_test-3
+. elif ${MACHINE} == arm64
+FAILING+=      run-cexp_test-{1,7}
+FAILING+=      run-ctrig_test-{1,5}
+FAILING+=      run-fma_test-{1,2,4,5,6,7}
+FAILING+=      run-invtrig_test-7
+FAILING+=      run-logarithm_test-5
+FAILING+=      run-lrint_test-1
+FAILING+=      run-next_test-{1,2,4}
+FAILING+=      run-rem_test-1
+FAILING+=      run-trig_test-{1,2}
+. elif ${MACHINE} == i386
+FAILING+=      run-cexp_test-{1,7}
+FAILING+=      run-ctrig_test-{1,5}
+FAILING+=      run-fma_test-{1,6}
+FAILING+=      run-invtrig_test-4
+FAILING+=      run-logarithm_test-3
+FAILING+=      run-lrint_test-1
+FAILING+=      run-next_test-{1,2,4}
+. elif ${MACHINE} == powerpc64
+FAILING+=      run-ctrig_test-{1,5}
+FAILING+=      run-invtrig_test-7
+FAILING+=      run-trig_test-2
+. elif ${MACHINE} == sparc64
+FAILING+=      run-ctrig_test-5
+FAILING+=      run-fma_test-{1,2,4,5,6}
+FAILING+=      run-invtrig_test-7
+FAILING+=      run-logarithm_test-5
+FAILING+=      run-lrint_test-1
+FAILING+=      run-next_test-{1,2,4,5}
+FAILING+=      run-rem_test-{1,2}
+FAILING+=      run-trig_test-{1,2}
+. endif
+
+. for t in run-cexp_test-{2,3,4}
+${t}:
+       # cexp functions don't handle inf and nan cases
+       @echo DISABLED
+. endfor
+
+. for t in ${FAILING}
+${t}:
+       # XXX: Doesn't work for unknown reasons
+       @echo DISABLED
+. endfor
 
-TESTS =
-TESTS +=       conj_test
-TESTS +=       fenv_test
-TESTS +=       ilogb_test
-TESTS +=       lrint_test
 
-PROGS= ${TESTS}
 LDADD= -lm
 DPADD= ${LIBM}
+CFLAGS+=       -fno-builtin
 
 .for t in ${TESTS}
 REGRESS_TARGETS+=      run-$t
@@ -16,4 +113,49 @@ run-$t: $t
        ./$t
 .endfor
 
+. for p in ${PROGS}
+SRCS_$p =              $p.c atf-c.c
+. endfor
+
+SRCS_rem_test += fls.c
+
+.for p in ${PROGS}
+run-$p: $p
+       ulimit -c unlimited && \
+       ntests="`./$p -n`" && \
+       echo "1..$$ntests" && \
+       tnumbers="`jot -ns' ' - 1 $$ntests`" && \
+       ${.MAKE} -C ${.CURDIR} PROG=$p NUMBERS="$$tnumbers" regress
+.endfor
+
+.if defined(NUMBERS)
+CUR_USER !=            id -g
+
+. for n in ${NUMBERS}
+DESCR_$n !=            eval `./${PROG} -i $n` && echo $$DESCR
+REQ_USER_$n !=         eval `./${PROG} -i $n` && echo $$REQ_USER
+
+.  if ${REQ_USER_$n} == "root"
+REGRESS_ROOT_TARGETS +=        run-${PROG}-$n
+.  endif
+
+run-${PROG}-$n:
+       @echo "$n ${DESCR_$n}"
+.  if ${REQ_USER_$n} == "root"
+       ${SUDO} ./${PROG} -r $n
+.  elif ${REQ_USER_$n} == "unprivileged" && ${CUR_USER} == 0
+       ${SUDO} su ${BUILDUSER} -c exec ./${PROG} -r $n
+.  elif ${REQ_USER_$n} == "unprivileged" || ${REQ_USER_$n} == ""
+       ./${PROG} -r $n
+.  else
+       # bad REQ_USER: ${REQ_USER_$n}
+       false
+.  endif
+
+cleanup-${PROG}-$n:
+       -./${PROG} -c $n
+
+. endfor
+.endif
+
 .include <bsd.regress.mk>
diff --git a/regress/lib/libm/msun/atf-c.c b/regress/lib/libm/msun/atf-c.c
new file mode 100644 (file)
index 0000000..f8774da
--- /dev/null
@@ -0,0 +1,128 @@
+/*     $OpenBSD: atf-c.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $ */
+/*
+ * Copyright (c) 2019-2021 Moritz Buhl <mbuhl@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include "atf-c.h"
+
+void usage(void);
+
+int cleanup;
+int count;
+int inspect;
+int run;
+int test;
+
+int
+main(int argc, char *argv[])
+{
+       int ch, test;
+       const char *errstr, *num;
+
+       while ((ch = getopt(argc, argv, "c:i:nr:")) != -1) {
+               switch(ch) {
+               case 'c':
+                       cleanup = 1;
+                       num = optarg;
+                       break;
+               case 'i':
+                       inspect = 1;
+                       num = optarg;
+                       break;
+               case 'n':
+                       count = 1;
+                       break;
+               case 'r':
+                       run = 1;
+                       num = optarg;
+                       break;
+               default:
+                       usage();
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (cleanup + count + inspect + run > 1)
+               usage();
+
+       if (cleanup || inspect || run) {
+               test = strtonum(num, 1, INT_MAX, &errstr);
+               if (errstr != NULL)
+                       errx(1, "test # is %s: %s", errstr, argv[1]);
+       }
+       if (count)
+               printf("%d\n", atf_test(0, 0));
+       else if (cleanup)
+               ATF_CLEANUP(test);
+       else if (run)
+               ATF_RUN(test);
+       else if (inspect)
+               ATF_INSPECT(test);
+       else
+               usage();
+
+       return 0;
+}
+
+void
+usage(void)
+{
+       fprintf(stderr, "usage: %s [-n] [-c|i|r test#]\n", getprogname());
+       exit(1);
+}
+
+void
+atf_require(int exp, int expected_errno, const char *expstr, const char *src,
+    const int lineno, char *fmt, ...)
+{
+       va_list args;
+       if (!(exp)) {
+               fprintf(stderr, "\n%s:%d: ", src, lineno);
+               if (fmt != NULL) {
+                       va_start(args, fmt);
+                       vfprintf(stderr, fmt, args);
+                       va_end(args);
+               } else {
+                       fprintf(stderr, "'%s' evaluated to false\n", expstr);
+               }
+               exit(1);
+       } else if (expected_errno >= 0 && errno != expected_errno) {
+               fprintf(stderr, "\n%s:%d: ", src, lineno);
+               fprintf(stderr, "expected errno %d but got %d instead\n",
+                   expected_errno, errno);
+               exit(1);
+       }
+       return;
+}
+
+void
+atf_tc_fail(char *fmt, ...)
+{
+       va_list args;
+       va_start(args, fmt);
+       verrx(1, fmt, args);
+}
diff --git a/regress/lib/libm/msun/atf-c.h b/regress/lib/libm/msun/atf-c.h
new file mode 100644 (file)
index 0000000..11bf1d8
--- /dev/null
@@ -0,0 +1,110 @@
+/*     $OpenBSD: atf-c.h,v 1.1 2021/10/22 18:00:22 mbuhl Exp $ */
+/*
+ * Copyright (c) 2019-2021 Moritz Buhl <mbuhl@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if !defined(ATF_C_H)
+#define ATF_C_H
+
+#include <pwd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int atf_test(int, int);
+void atf_require(int, int, const char *, const char *, const int, char *, ...);
+void atf_tc_fail(char *, ...)
+    __attribute__((__noreturn__, __format__ (printf, 1, 2)));
+#define atf_tc_fail_check(file, line, fmt, ...)                                \
+       atf_tc_fail(file ":%d " fmt, line, ##__VA_ARGS__)
+#define atf_tc_get_config_var_as_bool_wd(a, b ,c)      c
+
+#define ATF_INSPECT_TEST       1
+#define ATF_RUN_TEST           2
+#define ATF_CLEANUP_TEST       3
+
+#define ATF_TC_FUNCTIONS(fn)                                           \
+void atf_head_##fn(void);                                              \
+void atf_body_##fn(void);                                              \
+void atf_cleanup_##fn(void);
+
+#define ATF_TC(fn)                                                     \
+ATF_TC_FUNCTIONS(fn)                                                   \
+void atf_cleanup_##fn(void) { return; }
+
+#define ATF_TC_WITH_CLEANUP(fn)                                                \
+ATF_TC_FUNCTIONS(fn)
+
+#define ATF_TC_WITHOUT_HEAD(fn)        void atf_cleanup_##fn(void) {}          \
+       void atf_head_##fn(void) {}
+#define ATF_TC_HEAD(fn, tc)    void atf_head_##fn(void)
+#define ATF_TC_BODY(fn, tc)    void atf_body_##fn(void)
+#define ATF_TC_CLEANUP(fn, tc) void atf_cleanup_##fn(void)
+
+#define ATF_TP_ADD_TCS(tp)     int atf_test(int tst, int what)
+#define ATF_TP_ADD_TC(tp, fn)  tst--;                                  \
+       if (tst == 0) {                                                 \
+               if (what == ATF_INSPECT_TEST)                           \
+                       atf_head_##fn();                                \
+               else if (what == ATF_RUN_TEST)                          \
+                       atf_body_##fn();                                \
+               else if (what == ATF_CLEANUP_TEST)                      \
+                       atf_cleanup_##fn();                             \
+               return 0;                                               \
+       }
+
+#define atf_no_error() (-tst)
+
+#define ATF_INSPECT(i)         atf_test(i, ATF_INSPECT_TEST)
+#define ATF_RUN(i)             atf_test(i, ATF_RUN_TEST)
+#define ATF_CLEANUP(i)         atf_test(i, ATF_CLEANUP_TEST)
+
+#define atf_tc_set_md_var(tc, attr, fmt, ...)                          \
+       if (strcmp(attr, "descr") == 0)                                 \
+               printf("DESCR=\"" fmt "\"\n", ##__VA_ARGS__);           \
+       else if (strcmp(attr, "require.user") == 0)                     \
+               printf("REQ_USER=" fmt "\n", ##__VA_ARGS__);
+
+#define ATF_CHECK              ATF_REQUIRE
+#define ATF_CHECK_MSG          ATF_REQUIRE_MSG
+#define ATF_CHECK_EQ           ATF_REQUIRE_EQ
+#define ATF_CHECK_ERRNO                ATF_REQUIRE_ERRNO
+#define ATF_CHECK_EQ_MSG       ATF_REQUIRE_EQ_MSG
+#define ATF_CHECK_STREQ        ATF_REQUIRE_STREQ
+
+#define atf_req(exp, err, msg, ...)                                    \
+       atf_require(exp, err, #exp, __FILE__, __LINE__, NULL)
+#define ATF_REQUIRE(exp)               atf_req(exp, -1, NULL)
+#define ATF_REQUIRE_ERRNO(no, exp)     atf_req(exp, no, NULL)
+#define ATF_REQUIRE_MSG(exp, fmt, ...) atf_req(exp, -1, fmt, ##__VA_ARGS__)
+#define ATF_REQUIRE_EQ(a, b)           atf_req((a) == (b), -1, NULL)
+#define ATF_REQUIRE_EQ_MSG(a, b, fmt, ...)                             \
+       atf_req((a) == (b), -1, fmt, ##__VA_ARGS__)
+#define ATF_REQUIRE_STREQ(x, y) \
+       ATF_REQUIRE_MSG(strcmp(x, y) == 0, "%s != %s (%s != %s)", #x, #y, x, y)
+
+#define atf_tc_fail_nonfatal(fmt, ...) atf_tc_fail(fmt, ##__VA_ARGS__)
+#define atf_tc_expect_fail(fmt, ...)   \
+       atf_tc_fail(fmt "\nEXPECTED_FAIL", ##__VA_ARGS__)
+#define atf_tc_skip(fmt, ...)          \
+       atf_tc_fail(fmt "\nSKIPPING", ##__VA_ARGS__)
+#define atf_tc_pass()                  exit(0)
+
+#define atf_tc_get_config_var(a, b)    "."
+
+#define atf_utils_fork()       fork()
+
+#endif /* !defined(ATF_C_H) */
diff --git a/regress/lib/libm/msun/cexp_test.c b/regress/lib/libm/msun/cexp_test.c
new file mode 100644 (file)
index 0000000..f943629
--- /dev/null
@@ -0,0 +1,315 @@
+/*     $OpenBSD: cexp_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $     */
+/*-
+ * Copyright (c) 2008-2011 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Tests for corner cases in cexp*().
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <complex.h>
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "test-utils.h"
+
+#pragma STDC FENV_ACCESS       ON
+#pragma        STDC CX_LIMITED_RANGE   OFF
+
+/*
+ * Test that a function returns the correct value and sets the
+ * exception flags correctly. The exceptmask specifies which
+ * exceptions we should check. We need to be lenient for several
+ * reasons, but mainly because on some architectures it's impossible
+ * to raise FE_OVERFLOW without raising FE_INEXACT. In some cases,
+ * whether cexp() raises an invalid exception is unspecified.
+ *
+ * These are macros instead of functions so that assert provides more
+ * meaningful error messages.
+ *
+ * XXX The volatile here is to avoid gcc's bogus constant folding and work
+ *     around the lack of support for the FENV_ACCESS pragma.
+ */
+#define        test_t(type, func, z, result, exceptmask, excepts, checksign)   \
+do {                                                                   \
+       volatile long double complex _d = z;                            \
+       volatile type complex _r = result;                              \
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));                \
+       CHECK_CFPEQUAL_CS((func)(_d), (_r), (checksign));               \
+       CHECK_FP_EXCEPTIONS_MSG(excepts, exceptmask, "for %s(%s)",      \
+           #func, #z);                                                 \
+} while (0)
+
+#define        test(func, z, result, exceptmask, excepts, checksign)           \
+       test_t(double, func, z, result, exceptmask, excepts, checksign)
+
+#define        test_f(func, z, result, exceptmask, excepts, checksign)         \
+       test_t(float, func, z, result, exceptmask, excepts, checksign)
+
+#define        test_l(func, z, result, exceptmask, excepts, checksign)         \
+       test_t(long double, func, z, result, exceptmask, excepts,       \
+           checksign)
+/* Test within a given tolerance. */
+#define        test_tol(func, z, result, tol)  do {                    \
+       CHECK_CFPEQUAL_TOL((func)(z), (result), (tol),          \
+           FPE_ABS_ZERO | CS_BOTH);                            \
+} while (0)
+
+/* Test all the functions that compute cexp(x). */
+#define        testall(x, result, exceptmask, excepts, checksign)      do {    \
+       test(cexp, x, result, exceptmask, excepts, checksign);          \
+       test_f(cexpf, x, result, exceptmask, excepts, checksign);       \
+       test_l(cexpl, x, result, exceptmask, excepts, checksign);       \
+} while (0)
+
+/*
+ * Test all the functions that compute cexp(x), within a given tolerance.
+ * The tolerance is specified in ulps.
+ */
+#define        testall_tol(x, result, tol)                             do {    \
+       test_tol(cexp, x, result, tol * DBL_ULP());                     \
+       test_tol(cexpf, x, result, tol * FLT_ULP());                    \
+} while (0)
+
+/* Various finite non-zero numbers to test. */
+static const float finites[] =
+{ -42.0e20, -1.0, -1.0e-10, -0.0, 0.0, 1.0e-10, 1.0, 42.0e20 };
+
+
+/* Tests for 0 */
+ATF_TC_WITHOUT_HEAD(zero);
+ATF_TC_BODY(zero, tc)
+{
+
+       /* cexp(0) = 1, no exceptions raised */
+       testall(0.0, 1.0, ALL_STD_EXCEPT, 0, 1);
+       testall(-0.0, 1.0, ALL_STD_EXCEPT, 0, 1);
+       testall(CMPLXL(0.0, -0.0), CMPLXL(1.0, -0.0), ALL_STD_EXCEPT, 0, 1);
+       testall(CMPLXL(-0.0, -0.0), CMPLXL(1.0, -0.0), ALL_STD_EXCEPT, 0, 1);
+}
+
+/*
+ * Tests for NaN.  The signs of the results are indeterminate unless the
+ * imaginary part is 0.
+ */
+ATF_TC_WITHOUT_HEAD(nan);
+ATF_TC_BODY(nan, tc)
+{
+       unsigned i;
+
+       /* cexp(x + NaNi) = NaN + NaNi and optionally raises invalid */
+       /* cexp(NaN + yi) = NaN + NaNi and optionally raises invalid (|y|>0) */
+       for (i = 0; i < nitems(finites); i++) {
+               testall(CMPLXL(finites[i], NAN), CMPLXL(NAN, NAN),
+                       ALL_STD_EXCEPT & ~FE_INVALID, 0, 0);
+               if (finites[i] == 0.0)
+                       continue;
+#ifndef __OpenBSD__
+               /* XXX FE_INEXACT shouldn't be raised here */
+               testall(CMPLXL(NAN, finites[i]), CMPLXL(NAN, NAN),
+                       ALL_STD_EXCEPT & ~(FE_INVALID | FE_INEXACT), 0, 0);
+#else
+               testall(CMPLXL(NAN, finites[i]), CMPLXL(NAN, NAN),
+                       ALL_STD_EXCEPT & ~(FE_INVALID), 0, 0);
+#endif
+       }
+
+       /* cexp(NaN +- 0i) = NaN +- 0i */
+       testall(CMPLXL(NAN, 0.0), CMPLXL(NAN, 0.0), ALL_STD_EXCEPT, 0, 1);
+       testall(CMPLXL(NAN, -0.0), CMPLXL(NAN, -0.0), ALL_STD_EXCEPT, 0, 1);
+
+       /* cexp(inf + NaN i) = inf + nan i */
+       testall(CMPLXL(INFINITY, NAN), CMPLXL(INFINITY, NAN),
+               ALL_STD_EXCEPT, 0, 0);
+       /* cexp(-inf + NaN i) = 0 */
+       testall(CMPLXL(-INFINITY, NAN), CMPLXL(0.0, 0.0),
+               ALL_STD_EXCEPT, 0, 0);
+       /* cexp(NaN + NaN i) = NaN + NaN i */
+       testall(CMPLXL(NAN, NAN), CMPLXL(NAN, NAN),
+               ALL_STD_EXCEPT, 0, 0);
+}
+
+ATF_TC_WITHOUT_HEAD(inf);
+ATF_TC_BODY(inf, tc)
+{
+       unsigned i;
+
+       /* cexp(x + inf i) = NaN + NaNi and raises invalid */
+       for (i = 0; i < nitems(finites); i++) {
+               testall(CMPLXL(finites[i], INFINITY), CMPLXL(NAN, NAN),
+                       ALL_STD_EXCEPT, FE_INVALID, 1);
+       }
+       /* cexp(-inf + yi) = 0 * (cos(y) + sin(y)i) */
+       /* XXX shouldn't raise an inexact exception */
+       testall(CMPLXL(-INFINITY, M_PI_4), CMPLXL(0.0, 0.0),
+               ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1);
+       testall(CMPLXL(-INFINITY, 3 * M_PI_4), CMPLXL(-0.0, 0.0),
+               ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1);
+       testall(CMPLXL(-INFINITY, 5 * M_PI_4), CMPLXL(-0.0, -0.0),
+               ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1);
+       testall(CMPLXL(-INFINITY, 7 * M_PI_4), CMPLXL(0.0, -0.0),
+               ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1);
+       testall(CMPLXL(-INFINITY, 0.0), CMPLXL(0.0, 0.0),
+               ALL_STD_EXCEPT, 0, 1);
+       testall(CMPLXL(-INFINITY, -0.0), CMPLXL(0.0, -0.0),
+               ALL_STD_EXCEPT, 0, 1);
+       /* cexp(inf + yi) = inf * (cos(y) + sin(y)i) (except y=0) */
+       /* XXX shouldn't raise an inexact exception */
+       testall(CMPLXL(INFINITY, M_PI_4), CMPLXL(INFINITY, INFINITY),
+               ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1);
+       testall(CMPLXL(INFINITY, 3 * M_PI_4), CMPLXL(-INFINITY, INFINITY),
+               ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1);
+       testall(CMPLXL(INFINITY, 5 * M_PI_4), CMPLXL(-INFINITY, -INFINITY),
+               ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1);
+       testall(CMPLXL(INFINITY, 7 * M_PI_4), CMPLXL(INFINITY, -INFINITY),
+               ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1);
+       /* cexp(inf + 0i) = inf + 0i */
+       testall(CMPLXL(INFINITY, 0.0), CMPLXL(INFINITY, 0.0),
+               ALL_STD_EXCEPT, 0, 1);
+       testall(CMPLXL(INFINITY, -0.0), CMPLXL(INFINITY, -0.0),
+               ALL_STD_EXCEPT, 0, 1);
+}
+
+ATF_TC_WITHOUT_HEAD(reals);
+ATF_TC_BODY(reals, tc)
+{
+       unsigned i;
+
+       for (i = 0; i < nitems(finites); i++) {
+               /* XXX could check exceptions more meticulously */
+               test(cexp, CMPLXL(finites[i], 0.0),
+                    CMPLXL(exp(finites[i]), 0.0),
+                    FE_INVALID | FE_DIVBYZERO, 0, 1);
+               test(cexp, CMPLXL(finites[i], -0.0),
+                    CMPLXL(exp(finites[i]), -0.0),
+                    FE_INVALID | FE_DIVBYZERO, 0, 1);
+               test_f(cexpf, CMPLXL(finites[i], 0.0),
+                    CMPLXL(expf(finites[i]), 0.0),
+                    FE_INVALID | FE_DIVBYZERO, 0, 1);
+               test_f(cexpf, CMPLXL(finites[i], -0.0),
+                    CMPLXL(expf(finites[i]), -0.0),
+                    FE_INVALID | FE_DIVBYZERO, 0, 1);
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(imaginaries);
+ATF_TC_BODY(imaginaries, tc)
+{
+       unsigned i;
+
+       for (i = 0; i < nitems(finites); i++) {
+               test(cexp, CMPLXL(0.0, finites[i]),
+                    CMPLXL(cos(finites[i]), sin(finites[i])),
+                    ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1);
+               test(cexp, CMPLXL(-0.0, finites[i]),
+                    CMPLXL(cos(finites[i]), sin(finites[i])),
+                    ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1);
+               test_f(cexpf, CMPLXL(0.0, finites[i]),
+                    CMPLXL(cosf(finites[i]), sinf(finites[i])),
+                    ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1);
+               test_f(cexpf, CMPLXL(-0.0, finites[i]),
+                    CMPLXL(cosf(finites[i]), sinf(finites[i])),
+                    ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1);
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(small);
+ATF_TC_BODY(small, tc)
+{
+       static const double tests[] = {
+            /* csqrt(a + bI) = x + yI */
+            /* a       b       x                       y */
+                1.0,   M_PI_4, M_SQRT2 * 0.5 * M_E,    M_SQRT2 * 0.5 * M_E,
+               -1.0,   M_PI_4, M_SQRT2 * 0.5 / M_E,    M_SQRT2 * 0.5 / M_E,
+                2.0,   M_PI_2, 0.0,                    M_E * M_E,
+                M_LN2, M_PI,   -2.0,                   0.0,
+       };
+       double a, b;
+       double x, y;
+       unsigned i;
+
+       for (i = 0; i < nitems(tests); i += 4) {
+               a = tests[i];
+               b = tests[i + 1];
+               x = tests[i + 2];
+               y = tests[i + 3];
+               test_tol(cexp, CMPLXL(a, b), CMPLXL(x, y), 3 * DBL_ULP());
+
+               /* float doesn't have enough precision to pass these tests */
+               if (x == 0 || y == 0)
+                       continue;
+               test_tol(cexpf, CMPLXL(a, b), CMPLXL(x, y), 1 * FLT_ULP());
+        }
+}
+
+/* Test inputs with a real part r that would overflow exp(r). */
+ATF_TC_WITHOUT_HEAD(large);
+ATF_TC_BODY(large, tc)
+{
+
+       test_tol(cexp, CMPLXL(709.79, 0x1p-1074),
+                CMPLXL(INFINITY, 8.94674309915433533273e-16), DBL_ULP());
+       test_tol(cexp, CMPLXL(1000, 0x1p-1074),
+                CMPLXL(INFINITY, 9.73344457300016401328e+110), DBL_ULP());
+       test_tol(cexp, CMPLXL(1400, 0x1p-1074),
+                CMPLXL(INFINITY, 5.08228858149196559681e+284), DBL_ULP());
+       test_tol(cexp, CMPLXL(900, 0x1.23456789abcdep-1020),
+                CMPLXL(INFINITY, 7.42156649354218408074e+83), DBL_ULP());
+       test_tol(cexp, CMPLXL(1300, 0x1.23456789abcdep-1020),
+                CMPLXL(INFINITY, 3.87514844965996756704e+257), DBL_ULP());
+
+       test_tol(cexpf, CMPLXL(88.73, 0x1p-149),
+                CMPLXL(INFINITY, 4.80265603e-07), 2 * FLT_ULP());
+       test_tol(cexpf, CMPLXL(90, 0x1p-149),
+                CMPLXL(INFINITY, 1.7101492622e-06f), 2 * FLT_ULP());
+       test_tol(cexpf, CMPLXL(192, 0x1p-149),
+                CMPLXL(INFINITY, 3.396809344e+38f), 2 * FLT_ULP());
+       test_tol(cexpf, CMPLXL(120, 0x1.234568p-120),
+                CMPLXL(INFINITY, 1.1163382522e+16f), 2 * FLT_ULP());
+       test_tol(cexpf, CMPLXL(170, 0x1.234568p-120),
+                CMPLXL(INFINITY, 5.7878851079e+37f), 2 * FLT_ULP());
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, zero);
+       ATF_TP_ADD_TC(tp, nan);
+       ATF_TP_ADD_TC(tp, inf);
+       ATF_TP_ADD_TC(tp, reals);
+       ATF_TP_ADD_TC(tp, imaginaries);
+       ATF_TP_ADD_TC(tp, small);
+       ATF_TP_ADD_TC(tp, large);
+
+       return (atf_no_error());
+}
index 709b21c..ce21351 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: conj_test.c,v 1.2 2019/02/21 17:36:41 bluhm Exp $     */
+/*     $OpenBSD: conj_test.c,v 1.3 2021/10/22 18:00:22 mbuhl Exp $     */
 /*-
  * Copyright (c) 2008 David Schultz <das@FreeBSD.org>
  * All rights reserved.
@@ -32,7 +32,6 @@
 #include <sys/cdefs.h>
 /* $FreeBSD: head/lib/msun/tests/conj_test.c 314650 2017-03-04 10:07:46Z ngie $ */
 
-#include <assert.h>
 #include <complex.h>
 #include <fenv.h>
 #include <math.h>
 
 #pragma        STDC CX_LIMITED_RANGE   OFF
 
-/*
- * Test that a function returns the correct value and sets the
- * exception flags correctly. The exceptmask specifies which
- * exceptions we should check. We need to be lenient for several
- * reasons, but mainly because on some architectures it's impossible
- * to raise FE_OVERFLOW without raising FE_INEXACT. In some cases,
- * whether cexp() raises an invalid exception is unspecified.
- *
- * These are macros instead of functions so that assert provides more
- * meaningful error messages.
- */
-#define        test(func, z, result, exceptmask, excepts)              do {    \
-       assert(feclearexcept(FE_ALL_EXCEPT) == 0);                      \
-       assert(cfpequal((func)(z), (result)));                          \
-       assert(((void)(func), fetestexcept(exceptmask) == (excepts)));  \
-} while (0)
-
-/* Make sure gcc doesn't use builtin versions of these or honor __pure2. */
 static float complex (*libconjf)(float complex) = conjf;
 static double complex (*libconj)(double complex) = conj;
 static long double complex (*libconjl)(long double complex) = conjl;
@@ -88,40 +69,54 @@ static const double tests[] = {
        -INFINITY, INFINITY,
 };
 
-int
-main(void)
+ATF_TC_WITHOUT_HEAD(main);
+ATF_TC_BODY(main, tc)
 {
        static const int ntests = sizeof(tests) / sizeof(tests[0]) / 2;
        complex float in;
        complex long double expected;
        int i;
 
-       printf("1..%d\n", ntests * 3);
-
        for (i = 0; i < ntests; i++) {
                __real__ expected = __real__ in = tests[2 * i];
                __imag__ in = tests[2 * i + 1];
                __imag__ expected = -cimag(in);
 
-               assert(fpequal(libcrealf(in), __real__ in));
-               assert(fpequal(libcreal(in), __real__ in));
-               assert(fpequal(libcreall(in), __real__ in));
-               assert(fpequal(libcimagf(in), __imag__ in));
-               assert(fpequal(libcimag(in), __imag__ in));
-               assert(fpequal(libcimagl(in), __imag__ in));
-
-               test(libconjf, in, expected, FE_ALL_EXCEPT, 0);
-               printf("ok %d\t\t# conjf(%#.2g + %#.2gI)\n",
-                   3 * i + 1, creal(in), cimag(in));
-
-               test(libconj, in, expected, FE_ALL_EXCEPT, 0);
-               printf("ok %d\t\t# conj(%#.2g + %#.2gI)\n",
-                   3 * i + 2, creal(in), cimag(in));
-
-               test(libconjl, in, expected, FE_ALL_EXCEPT, 0);
-               printf("ok %d\t\t# conjl(%#.2g + %#.2gI)\n",
-                   3 * i + 3, creal(in), cimag(in));
-       }
+               ATF_REQUIRE(fpequal_cs(libcrealf(in), __real__ in, true));
+               ATF_REQUIRE(fpequal_cs(libcreal(in), __real__ in, true));
+               ATF_REQUIRE(fpequal_cs(libcreall(in), __real__ in, true));
+               ATF_REQUIRE(fpequal_cs(libcimagf(in), __imag__ in, true));
+               ATF_REQUIRE(fpequal_cs(libcimag(in), __imag__ in, true));
+               ATF_REQUIRE(fpequal_cs(libcimagl(in), __imag__ in, true));
+               ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+               ATF_REQUIRE_MSG(
+                   cfpequal(libconjf(in), expected),
+                   "conjf(%#.2g + %#.2gI): wrong value", creal(in), cimag(in)
+               );
+               ATF_REQUIRE_EQ_MSG(0, fetestexcept(FE_ALL_EXCEPT),
+                   "conj(%#.2g + %#.2gI): threw an exception: %#x", creal(in),
+                   cimag(in), fetestexcept(FE_ALL_EXCEPT));
+               ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+               ATF_REQUIRE_MSG(cfpequal(libconj(in), expected),
+                   "conj(%#.2g + %#.2gI): wrong value", creal(in), cimag(in));
+               ATF_REQUIRE_EQ_MSG(0, fetestexcept(FE_ALL_EXCEPT),
+                   "conj(%#.2g + %#.2gI): threw an exception: %#x", creal(in),
+                   cimag(in), fetestexcept(FE_ALL_EXCEPT));
+               ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+               ATF_REQUIRE_MSG(cfpequal(libconjl(in), expected),
+                   "conjl(%#.2g + %#.2gI): wrong value", creal(in), cimag(in));
+               ATF_REQUIRE_EQ_MSG(0, fetestexcept(FE_ALL_EXCEPT),
+                   "conjl(%#.2g + %#.2gI): threw an exception: %#x", creal(in),
+                   cimag(in), fetestexcept(FE_ALL_EXCEPT));
+       }
+}
 
-       return (0);
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, main);
+       return (atf_no_error());
 }
diff --git a/regress/lib/libm/msun/csqrt_test.c b/regress/lib/libm/msun/csqrt_test.c
new file mode 100644 (file)
index 0000000..adbd427
--- /dev/null
@@ -0,0 +1,369 @@
+/*     $OpenBSD: csqrt_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $    */
+/*-
+ * Copyright (c) 2007 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <complex.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "test-utils.h"
+
+/*
+ * This is a test hook that can point to csqrtl(), _csqrt(), or to _csqrtf().
+ * The latter two convert to float or double, respectively, and test csqrtf()
+ * and csqrt() with the same arguments.
+ */
+static long double complex (*t_csqrt)(long double complex);
+
+static long double complex
+_csqrtf(long double complex d)
+{
+
+       return (csqrtf((float complex)d));
+}
+
+static long double complex
+_csqrt(long double complex d)
+{
+
+       return (csqrt((double complex)d));
+}
+
+#pragma        STDC CX_LIMITED_RANGE   OFF
+
+/*
+ * Compare d1 and d2 using special rules: NaN == NaN and +0 != -0.
+ * Fail an assertion if they differ.
+ */
+#define assert_equal(d1, d2) CHECK_CFPEQUAL_CS(d1, d2, CS_BOTH)
+
+/*
+ * Test csqrt for some finite arguments where the answer is exact.
+ * (We do not test if it produces correctly rounded answers when the
+ * result is inexact, nor do we check whether it throws spurious
+ * exceptions.)
+ */
+static void
+test_finite(void)
+{
+       static const double tests[] = {
+            /* csqrt(a + bI) = x + yI */
+            /* a       b       x       y */
+               0,      8,      2,      2,
+               0,      -8,     2,      -2,
+               4,      0,      2,      0,
+               -4,     0,      0,      2,
+               3,      4,      2,      1,
+               3,      -4,     2,      -1,
+               -3,     4,      1,      2,
+               -3,     -4,     1,      -2,
+               5,      12,     3,      2,
+               7,      24,     4,      3,
+               9,      40,     5,      4,
+               11,     60,     6,      5,
+               13,     84,     7,      6,
+               33,     56,     7,      4,
+               39,     80,     8,      5,
+               65,     72,     9,      4,
+               987,    9916,   74,     67,
+               5289,   6640,   83,     40,
+               460766389075.0, 16762287900.0, 678910, 12345
+       };
+       /*
+        * We also test some multiples of the above arguments. This
+        * array defines which multiples we use. Note that these have
+        * to be small enough to not cause overflow for float precision
+        * with all of the constants in the above table.
+        */
+       static const double mults[] = {
+               1,
+               2,
+               3,
+               13,
+               16,
+               0x1.p30,
+               0x1.p-30,
+       };
+
+       double a, b;
+       double x, y;
+       unsigned i, j;
+
+       for (i = 0; i < nitems(tests); i += 4) {
+               for (j = 0; j < nitems(mults); j++) {
+                       a = tests[i] * mults[j] * mults[j];
+                       b = tests[i + 1] * mults[j] * mults[j];
+                       x = tests[i + 2] * mults[j];
+                       y = tests[i + 3] * mults[j];
+                       ATF_CHECK(t_csqrt(CMPLXL(a, b)) == CMPLXL(x, y));
+               }
+       }
+
+}
+
+/*
+ * Test the handling of +/- 0.
+ */
+static void
+test_zeros(void)
+{
+
+       assert_equal(t_csqrt(CMPLXL(0.0, 0.0)), CMPLXL(0.0, 0.0));
+       assert_equal(t_csqrt(CMPLXL(-0.0, 0.0)), CMPLXL(0.0, 0.0));
+       assert_equal(t_csqrt(CMPLXL(0.0, -0.0)), CMPLXL(0.0, -0.0));
+       assert_equal(t_csqrt(CMPLXL(-0.0, -0.0)), CMPLXL(0.0, -0.0));
+}
+
+/*
+ * Test the handling of infinities when the other argument is not NaN.
+ */
+static void
+test_infinities(void)
+{
+       static const double vals[] = {
+               0.0,
+               -0.0,
+               42.0,
+               -42.0,
+               INFINITY,
+               -INFINITY,
+       };
+
+       unsigned i;
+
+       for (i = 0; i < nitems(vals); i++) {
+               if (isfinite(vals[i])) {
+                       assert_equal(t_csqrt(CMPLXL(-INFINITY, vals[i])),
+                           CMPLXL(0.0, copysignl(INFINITY, vals[i])));
+                       assert_equal(t_csqrt(CMPLXL(INFINITY, vals[i])),
+                           CMPLXL(INFINITY, copysignl(0.0, vals[i])));
+               }
+               assert_equal(t_csqrt(CMPLXL(vals[i], INFINITY)),
+                   CMPLXL(INFINITY, INFINITY));
+               assert_equal(t_csqrt(CMPLXL(vals[i], -INFINITY)),
+                   CMPLXL(INFINITY, -INFINITY));
+       }
+}
+
+/*
+ * Test the handling of NaNs.
+ */
+static void
+test_nans(void)
+{
+
+       ATF_CHECK(creall(t_csqrt(CMPLXL(INFINITY, NAN))) == INFINITY);
+       ATF_CHECK(isnan(cimagl(t_csqrt(CMPLXL(INFINITY, NAN)))));
+
+       ATF_CHECK(isnan(creall(t_csqrt(CMPLXL(-INFINITY, NAN)))));
+       ATF_CHECK(isinf(cimagl(t_csqrt(CMPLXL(-INFINITY, NAN)))));
+
+       assert_equal(t_csqrt(CMPLXL(NAN, INFINITY)),
+                    CMPLXL(INFINITY, INFINITY));
+       assert_equal(t_csqrt(CMPLXL(NAN, -INFINITY)),
+                    CMPLXL(INFINITY, -INFINITY));
+
+       assert_equal(t_csqrt(CMPLXL(0.0, NAN)), CMPLXL(NAN, NAN));
+       assert_equal(t_csqrt(CMPLXL(-0.0, NAN)), CMPLXL(NAN, NAN));
+       assert_equal(t_csqrt(CMPLXL(42.0, NAN)), CMPLXL(NAN, NAN));
+       assert_equal(t_csqrt(CMPLXL(-42.0, NAN)), CMPLXL(NAN, NAN));
+       assert_equal(t_csqrt(CMPLXL(NAN, 0.0)), CMPLXL(NAN, NAN));
+       assert_equal(t_csqrt(CMPLXL(NAN, -0.0)), CMPLXL(NAN, NAN));
+       assert_equal(t_csqrt(CMPLXL(NAN, 42.0)), CMPLXL(NAN, NAN));
+       assert_equal(t_csqrt(CMPLXL(NAN, -42.0)), CMPLXL(NAN, NAN));
+       assert_equal(t_csqrt(CMPLXL(NAN, NAN)), CMPLXL(NAN, NAN));
+}
+
+/*
+ * Test whether csqrt(a + bi) works for inputs that are large enough to
+ * cause overflow in hypot(a, b) + a.  Each of the tests is scaled up to
+ * near MAX_EXP.
+ */
+static void
+test_overflow(int maxexp)
+{
+       long double a, b;
+       long double complex result;
+       int exp, i;
+
+       ATF_CHECK(maxexp > 0 && maxexp % 2 == 0);
+
+       for (i = 0; i < 4; i++) {
+               exp = maxexp - 2 * i;
+
+               /* csqrt(115 + 252*I) == 14 + 9*I */
+               a = ldexpl(115 * 0x1p-8, exp);
+               b = ldexpl(252 * 0x1p-8, exp);
+               result = t_csqrt(CMPLXL(a, b));
+               ATF_CHECK_EQ(creall(result), ldexpl(14 * 0x1p-4, exp / 2));
+               ATF_CHECK_EQ(cimagl(result), ldexpl(9 * 0x1p-4, exp / 2));
+
+               /* csqrt(-11 + 60*I) = 5 + 6*I */
+               a = ldexpl(-11 * 0x1p-6, exp);
+               b = ldexpl(60 * 0x1p-6, exp);
+               result = t_csqrt(CMPLXL(a, b));
+               ATF_CHECK_EQ(creall(result), ldexpl(5 * 0x1p-3, exp / 2));
+               ATF_CHECK_EQ(cimagl(result), ldexpl(6 * 0x1p-3, exp / 2));
+
+               /* csqrt(225 + 0*I) == 15 + 0*I */
+               a = ldexpl(225 * 0x1p-8, exp);
+               b = 0;
+               result = t_csqrt(CMPLXL(a, b));
+               ATF_CHECK_EQ(creall(result), ldexpl(15 * 0x1p-4, exp / 2));
+               ATF_CHECK_EQ(cimagl(result), 0);
+       }
+}
+
+/*
+ * Test that precision is maintained for some large squares.  Set all or
+ * some bits in the lower mantdig/2 bits, square the number, and try to
+ * recover the sqrt.  Note:
+ *     (x + xI)**2 = 2xxI
+ */
+static void
+test_precision(int maxexp, int mantdig)
+{
+       long double b, x;
+       long double complex result;
+#if LDBL_MANT_DIG <= 64
+       typedef uint64_t ldbl_mant_type;
+#elif LDBL_MANT_DIG <= 128
+       typedef __uint128_t ldbl_mant_type;
+#else
+#error "Unsupported long double format"
+#endif
+       ldbl_mant_type mantbits, sq_mantbits;
+       int exp, i;
+
+       ATF_REQUIRE(maxexp > 0 && maxexp % 2 == 0);
+       ATF_REQUIRE(mantdig <= LDBL_MANT_DIG);
+       mantdig = rounddown(mantdig, 2);
+
+       for (exp = 0; exp <= maxexp; exp += 2) {
+               mantbits = ((ldbl_mant_type)1 << (mantdig / 2)) - 1;
+               for (i = 0; i < 100 &&
+                    mantbits > ((ldbl_mant_type)1 << (mantdig / 2 - 1));
+                    i++, mantbits--) {
+                       sq_mantbits = mantbits * mantbits;
+                       /*
+                        * sq_mantibts is a mantdig-bit number.  Divide by
+                        * 2**mantdig to normalize it to [0.5, 1), where,
+                        * note, the binary power will be -1.  Raise it by
+                        * 2**exp for the test.  exp is even.  Lower it by
+                        * one to reach a final binary power which is also
+                        * even.  The result should be exactly
+                        * representable, given that mantdig is less than or
+                        * equal to the available precision.
+                        */
+                       b = ldexpl((long double)sq_mantbits,
+                           exp - 1 - mantdig);
+                       x = ldexpl(mantbits, (exp - 2 - mantdig) / 2);
+                       CHECK_FPEQUAL(b, x * x * 2);
+                       result = t_csqrt(CMPLXL(0, b));
+                       CHECK_FPEQUAL(x, creall(result));
+                       CHECK_FPEQUAL(x, cimagl(result));
+               }
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(csqrt);
+ATF_TC_BODY(csqrt, tc)
+{
+       /* Test csqrt() */
+       t_csqrt = _csqrt;
+
+       test_finite();
+
+       test_zeros();
+
+       test_infinities();
+
+       test_nans();
+
+       test_overflow(DBL_MAX_EXP);
+
+       test_precision(DBL_MAX_EXP, DBL_MANT_DIG);
+}
+
+ATF_TC_WITHOUT_HEAD(csqrtf);
+ATF_TC_BODY(csqrtf, tc)
+{
+       /* Now test csqrtf() */
+       t_csqrt = _csqrtf;
+
+       test_finite();
+
+       test_zeros();
+
+       test_infinities();
+
+       test_nans();
+
+       test_overflow(FLT_MAX_EXP);
+
+       test_precision(FLT_MAX_EXP, FLT_MANT_DIG);
+}
+
+ATF_TC_WITHOUT_HEAD(csqrtl);
+ATF_TC_BODY(csqrtl, tc)
+{
+       /* Now test csqrtl() */
+       t_csqrt = csqrtl;
+
+       test_finite();
+
+       test_zeros();
+
+       test_infinities();
+
+       test_nans();
+
+       test_overflow(LDBL_MAX_EXP);
+
+       /* i386 is configured to use 53-bit rounding precision for long double. */
+#ifndef __i386__
+       test_precision(LDBL_MAX_EXP, LDBL_MANT_DIG);
+#else
+       test_precision(LDBL_MAX_EXP, DBL_MANT_DIG);
+#endif
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, csqrt);
+       ATF_TP_ADD_TC(tp, csqrtf);
+       ATF_TP_ADD_TC(tp, csqrtl);
+
+       return (atf_no_error());
+}
diff --git a/regress/lib/libm/msun/ctrig_test.c b/regress/lib/libm/msun/ctrig_test.c
new file mode 100644 (file)
index 0000000..d571d64
--- /dev/null
@@ -0,0 +1,491 @@
+/*     $OpenBSD: ctrig_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $    */
+/*-
+ * Copyright (c) 2008-2011 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Tests for csin[h](), ccos[h](), and ctan[h]().
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <complex.h>
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "test-utils.h"
+
+#pragma STDC FENV_ACCESS       ON
+#pragma        STDC CX_LIMITED_RANGE   OFF
+
+/*
+ * Test that a function returns the correct value and sets the
+ * exception flags correctly. The exceptmask specifies which
+ * exceptions we should check. We need to be lenient for several
+ * reasons, but mainly because on some architectures it's impossible
+ * to raise FE_OVERFLOW without raising FE_INEXACT.
+ *
+ * These are macros instead of functions so that assert provides more
+ * meaningful error messages.
+ *
+ * XXX The volatile here is to avoid gcc's bogus constant folding and work
+ *     around the lack of support for the FENV_ACCESS pragma.
+ */
+#define test_p(func, z, result, exceptmask, excepts, checksign)                        \
+       do {                                                                    \
+               volatile long double complex _d = z;                            \
+               debug("  testing %s(%Lg + %Lg I) == %Lg + %Lg I\n", #func,      \
+                   creall(_d), cimagl(_d), creall(result), cimagl(result));    \
+               ATF_CHECK(feclearexcept(FE_ALL_EXCEPT) == 0);                   \
+               CHECK_CFPEQUAL_CS((func)(_d), (result), (checksign));           \
+               volatile int _e = fetestexcept(exceptmask);                     \
+               ATF_CHECK_MSG(_e == (excepts),                                  \
+                   "%s fetestexcept(%s) (%#x) != %#x", __XSTRING(func),        \
+                   __XSTRING(exceptmask), _e, (excepts));                      \
+       } while (0)
+
+/*
+ * Test within a given tolerance.  The tolerance indicates relative error
+ * in ulps.  If result is 0, however, it measures absolute error in units
+ * of <format>_EPSILON.
+ */
+#define        test_p_tol(func, z, result, tol)                        do {    \
+       debug("  testing %s(%Lg + %Lg I) ~= %Lg + %Lg I\n", #func,      \
+           creall(z), cimagl(z), creall(result), cimagl(result));      \
+       CHECK_CFPEQUAL_TOL((func)(z), (result), (tol), FPE_ABS_ZERO); \
+} while (0)
+
+/* These wrappers apply the identities f(conj(z)) = conj(f(z)). */
+#define        test(func, z, result, exceptmask, excepts, checksign)   do {    \
+       test_p(func, z, result, exceptmask, excepts, checksign);        \
+       test_p(func, conjl(z), conjl(result), exceptmask, excepts, checksign); \
+} while (0)
+#define        test_tol(func, z, result, tol)                          do {    \
+       test_p_tol(func, z, result, tol);                               \
+       test_p_tol(func, conjl(z), conjl(result), tol);                 \
+} while (0)
+#define        test_odd_tol(func, z, result, tol)                      do {    \
+       test_tol(func, z, result, tol);                                 \
+       test_tol(func, -(z), -(result), tol);                           \
+} while (0)
+#define        test_even_tol(func, z, result, tol)                     do {    \
+       test_tol(func, z, result, tol);                                 \
+       test_tol(func, -(z), result, tol);                              \
+} while (0)
+
+/* Test the given function in all precisions. */
+#define        testall(func, x, result, exceptmask, excepts, checksign) do {   \
+       test(func, x, result, exceptmask, excepts, checksign);          \
+       test(func##f, x, result, exceptmask, excepts, checksign);       \
+} while (0)
+#define        testall_odd(func, x, result, exceptmask, excepts, checksign) do { \
+       testall(func, x, result, exceptmask, excepts, checksign);       \
+       testall(func, -x, -result, exceptmask, excepts, checksign);     \
+} while (0)
+#define        testall_even(func, x, result, exceptmask, excepts, checksign) do { \
+       testall(func, x, result, exceptmask, excepts, checksign);       \
+       testall(func, -x, result, exceptmask, excepts, checksign);      \
+} while (0)
+
+/*
+ * Test the given function in all precisions, within a given tolerance.
+ * The tolerance is specified in ulps.
+ */
+#define        testall_tol(func, x, result, tol)                          do { \
+       test_tol(func, x, result, tol * DBL_ULP());                     \
+       test_tol(func##f, x, result, tol * FLT_ULP());                  \
+} while (0)
+#define        testall_odd_tol(func, x, result, tol)                      do { \
+       test_odd_tol(func, x, result, tol * DBL_ULP());                 \
+       test_odd_tol(func##f, x, result, tol * FLT_ULP());              \
+} while (0)
+#define        testall_even_tol(func, x, result, tol)                     do { \
+       test_even_tol(func, x, result, tol * DBL_ULP());                \
+       test_even_tol(func##f, x, result, tol * FLT_ULP());             \
+} while (0)
+
+
+ATF_TC(test_zero_input);
+ATF_TC_HEAD(test_zero_input, tc)
+{
+       atf_tc_set_md_var(tc, "descr", "test 0 input");
+}
+ATF_TC_BODY(test_zero_input, tc)
+{
+       long double complex zero = CMPLXL(0.0, 0.0);
+
+       /* csinh(0) = ctanh(0) = 0; ccosh(0) = 1 (no exceptions raised) */
+       testall_odd(csinh, zero, zero, ALL_STD_EXCEPT, 0, CS_BOTH);
+       testall_odd(csin, zero, zero, ALL_STD_EXCEPT, 0, CS_BOTH);
+       testall_even(ccosh, zero, 1.0, ALL_STD_EXCEPT, 0, CS_BOTH);
+       testall_even(ccos, zero, CMPLXL(1.0, -0.0), ALL_STD_EXCEPT, 0, CS_BOTH);
+       testall_odd(ctanh, zero, zero, ALL_STD_EXCEPT, 0, CS_BOTH);
+       testall_odd(ctan, zero, zero, ALL_STD_EXCEPT, 0, CS_BOTH);
+}
+
+ATF_TC(test_nan_inputs);
+ATF_TC_HEAD(test_nan_inputs, tc)
+{
+       atf_tc_set_md_var(tc, "descr", "test NaN inputs");
+}
+ATF_TC_BODY(test_nan_inputs, tc)
+{
+       long double complex nan_nan = CMPLXL(NAN, NAN);
+       long double complex z;
+
+       /*
+        * IN           CSINH           CCOSH           CTANH
+        * NaN,NaN      NaN,NaN         NaN,NaN         NaN,NaN
+        * finite,NaN   NaN,NaN [inval] NaN,NaN [inval] NaN,NaN [inval]
+        * NaN,finite   NaN,NaN [inval] NaN,NaN [inval] NaN,NaN [inval]
+        * NaN,Inf      NaN,NaN [inval] NaN,NaN [inval] NaN,NaN [inval]
+        * Inf,NaN      +-Inf,NaN       Inf,NaN         1,+-0
+        * 0,NaN        +-0,NaN         NaN,+-0         +-0,NaN
+        * NaN,0        NaN,0           NaN,+-0         NaN,+-0
+        */
+       z = nan_nan;
+       testall_odd(csinh, z, nan_nan, ALL_STD_EXCEPT, 0, 0);
+       testall_even(ccosh, z, nan_nan, ALL_STD_EXCEPT, 0, 0);
+       testall_odd(ctanh, z, nan_nan, ALL_STD_EXCEPT, 0, 0);
+       testall_odd(csin, z, nan_nan, ALL_STD_EXCEPT, 0, 0);
+       testall_even(ccos, z, nan_nan, ALL_STD_EXCEPT, 0, 0);
+       testall_odd(ctan, z, nan_nan, ALL_STD_EXCEPT, 0, 0);
+
+       z = CMPLXL(42, NAN);
+       testall_odd(csinh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_even(ccosh, z, nan_nan, OPT_INVALID, 0, 0);
+       /* XXX We allow a spurious inexact exception here. */
+       testall_odd(ctanh, z, nan_nan, OPT_INVALID & ~FE_INEXACT, 0, 0);
+       testall_odd(csin, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_even(ccos, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_odd(ctan, z, nan_nan, OPT_INVALID, 0, 0);
+
+       z = CMPLXL(NAN, 42);
+       testall_odd(csinh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_even(ccosh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_odd(ctanh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_odd(csin, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_even(ccos, z, nan_nan, OPT_INVALID, 0, 0);
+       /* XXX We allow a spurious inexact exception here. */
+       testall_odd(ctan, z, nan_nan, OPT_INVALID & ~FE_INEXACT, 0, 0);
+
+       z = CMPLXL(NAN, INFINITY);
+       testall_odd(csinh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_even(ccosh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_odd(ctanh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_odd(csin, z, CMPLXL(NAN, INFINITY), ALL_STD_EXCEPT, 0, 0);
+       testall_even(ccos, z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0,
+           CS_IMAG);
+       testall_odd(ctan, z, CMPLXL(0, 1), ALL_STD_EXCEPT, 0, CS_IMAG);
+
+       z = CMPLXL(INFINITY, NAN);
+       testall_odd(csinh, z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0, 0);
+       testall_even(ccosh, z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0,
+                    CS_REAL);
+       testall_odd(ctanh, z, CMPLXL(1, 0), ALL_STD_EXCEPT, 0, CS_REAL);
+       testall_odd(csin, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_even(ccos, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_odd(ctan, z, nan_nan, OPT_INVALID, 0, 0);
+
+       z = CMPLXL(0, NAN);
+       testall_odd(csinh, z, CMPLXL(0, NAN), ALL_STD_EXCEPT, 0, CS_REAL);
+       testall_even(ccosh, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, 0, 0);
+       testall_odd(ctanh, z, CMPLXL(0, NAN), OPT_INVALID, 0, CS_REAL);
+       testall_odd(csin, z, CMPLXL(0, NAN), ALL_STD_EXCEPT, 0, CS_REAL);
+       testall_even(ccos, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, 0, 0);
+       testall_odd(ctan, z, CMPLXL(0, NAN), ALL_STD_EXCEPT, 0, CS_REAL);
+
+       z = CMPLXL(NAN, 0);
+       testall_odd(csinh, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, 0, CS_IMAG);
+       testall_even(ccosh, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, 0, 0);
+       testall_odd(ctanh, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, 0, CS_IMAG);
+       testall_odd(csin, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, 0, 0);
+       testall_even(ccos, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, 0, 0);
+       testall_odd(ctan, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, 0, CS_IMAG);
+}
+
+ATF_TC(test_inf_inputs);
+ATF_TC_HEAD(test_inf_inputs, tc)
+{
+       atf_tc_set_md_var(tc, "descr", "test infinity inputs");
+}
+ATF_TC_BODY(test_inf_inputs, tc)
+{
+       static const long double finites[] = {
+           0, M_PI / 4, 3 * M_PI / 4, 5 * M_PI / 4,
+       };
+       long double complex z, c, s;
+       unsigned i;
+
+       /*
+        * IN           CSINH           CCOSH           CTANH
+        * Inf,Inf      +-Inf,NaN inval +-Inf,NaN inval 1,+-0
+        * Inf,finite   Inf cis(finite) Inf cis(finite) 1,0 sin(2 finite)
+        * 0,Inf        +-0,NaN inval   NaN,+-0 inval   +-0,NaN
+        * finite,Inf   NaN,NaN inval   NaN,NaN inval   NaN,NaN inval
+        */
+       z = CMPLXL(INFINITY, INFINITY);
+       testall_odd(csinh, z, CMPLXL(INFINITY, NAN),
+                   ALL_STD_EXCEPT, FE_INVALID, 0);
+       testall_even(ccosh, z, CMPLXL(INFINITY, NAN),
+                    ALL_STD_EXCEPT, FE_INVALID, 0);
+       testall_odd(ctanh, z, CMPLXL(1, 0), ALL_STD_EXCEPT, 0, CS_REAL);
+       testall_odd(csin, z, CMPLXL(NAN, INFINITY),
+                   ALL_STD_EXCEPT, FE_INVALID, 0);
+       testall_even(ccos, z, CMPLXL(INFINITY, NAN),
+                    ALL_STD_EXCEPT, FE_INVALID, 0);
+       testall_odd(ctan, z, CMPLXL(0, 1), ALL_STD_EXCEPT, 0, CS_REAL);
+
+       /* XXX We allow spurious inexact exceptions here (hard to avoid). */
+       for (i = 0; i < nitems(finites); i++) {
+               z = CMPLXL(INFINITY, finites[i]);
+               c = INFINITY * cosl(finites[i]);
+               s = finites[i] == 0 ? finites[i] : INFINITY * sinl(finites[i]);
+               testall_odd(csinh, z, CMPLXL(c, s), OPT_INEXACT, 0, CS_BOTH);
+               testall_even(ccosh, z, CMPLXL(c, s), OPT_INEXACT, 0, CS_BOTH);
+               testall_odd(ctanh, z, CMPLXL(1, 0 * sin(finites[i] * 2)),
+                           OPT_INEXACT, 0, CS_BOTH);
+               z = CMPLXL(finites[i], INFINITY);
+               testall_odd(csin, z, CMPLXL(s, c), OPT_INEXACT, 0, CS_BOTH);
+               testall_even(ccos, z, CMPLXL(c, -s), OPT_INEXACT, 0, CS_BOTH);
+               testall_odd(ctan, z, CMPLXL(0 * sin(finites[i] * 2), 1),
+                           OPT_INEXACT, 0, CS_BOTH);
+       }
+
+       z = CMPLXL(0, INFINITY);
+       testall_odd(csinh, z, CMPLXL(0, NAN), ALL_STD_EXCEPT, FE_INVALID, 0);
+       testall_even(ccosh, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, FE_INVALID, 0);
+       testall_odd(ctanh, z, CMPLXL(0, NAN), ALL_STD_EXCEPT, FE_INVALID, CS_REAL);
+       z = CMPLXL(INFINITY, 0);
+       testall_odd(csin, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, FE_INVALID, 0);
+       testall_even(ccos, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, FE_INVALID, 0);
+       testall_odd(ctan, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, FE_INVALID, CS_IMAG);
+
+       z = CMPLXL(42, INFINITY);
+       testall_odd(csinh, z, CMPLXL(NAN, NAN), ALL_STD_EXCEPT, FE_INVALID, 0);
+       testall_even(ccosh, z, CMPLXL(NAN, NAN), ALL_STD_EXCEPT, FE_INVALID, 0);
+       /* XXX We allow a spurious inexact exception here. */
+       testall_odd(ctanh, z, CMPLXL(NAN, NAN), OPT_INEXACT, FE_INVALID, 0);
+       z = CMPLXL(INFINITY, 42);
+       testall_odd(csin, z, CMPLXL(NAN, NAN), ALL_STD_EXCEPT, FE_INVALID, 0);
+       testall_even(ccos, z, CMPLXL(NAN, NAN), ALL_STD_EXCEPT, FE_INVALID, 0);
+       /* XXX We allow a spurious inexact exception here. */
+       testall_odd(ctan, z, CMPLXL(NAN, NAN), OPT_INEXACT, FE_INVALID, 0);
+}
+
+ATF_TC(test_axes);
+ATF_TC_HEAD(test_axes, tc)
+{
+       atf_tc_set_md_var(tc, "descr", "test along the real/imaginary axes");
+}
+ATF_TC_BODY(test_axes, tc)
+{
+       static const long double nums[] = {
+           M_PI / 4, M_PI / 2, 3 * M_PI / 4,
+           5 * M_PI / 4, 3 * M_PI / 2, 7 * M_PI / 4,
+       };
+       long double complex z;
+       unsigned i;
+
+       for (i = 0; i < nitems(nums); i++) {
+               /* Real axis */
+               z = CMPLXL(nums[i], 0.0);
+               test_odd_tol(csinh, z, CMPLXL(sinh(nums[i]), 0), DBL_ULP());
+               test_even_tol(ccosh, z, CMPLXL(cosh(nums[i]), 0), DBL_ULP());
+               test_odd_tol(ctanh, z, CMPLXL(tanh(nums[i]), 0), DBL_ULP());
+               test_odd_tol(csin, z, CMPLXL(sin(nums[i]),
+                   copysign(0, cos(nums[i]))), DBL_ULP());
+               test_even_tol(ccos, z, CMPLXL(cos(nums[i]),
+                   -copysign(0, sin(nums[i]))), DBL_ULP());
+               test_odd_tol(ctan, z, CMPLXL(tan(nums[i]), 0), DBL_ULP());
+
+               test_odd_tol(csinhf, z, CMPLXL(sinhf(nums[i]), 0), FLT_ULP());
+               test_even_tol(ccoshf, z, CMPLXL(coshf(nums[i]), 0), FLT_ULP());
+               printf("%a %a\n", creal(z), cimag(z));
+               printf("%a %a\n", creal(ctanhf(z)), cimag(ctanhf(z)));
+               printf("%a\n", nextafterf(tanhf(nums[i]), INFINITY));
+               test_odd_tol(ctanhf, z, CMPLXL(tanhf(nums[i]), 0),
+                            1.3 * FLT_ULP());
+               test_odd_tol(csinf, z, CMPLXL(sinf(nums[i]),
+                   copysign(0, cosf(nums[i]))), FLT_ULP());
+               test_even_tol(ccosf, z, CMPLXL(cosf(nums[i]),
+                   -copysign(0, sinf(nums[i]))), 2 * FLT_ULP());
+               test_odd_tol(ctanf, z, CMPLXL(tanf(nums[i]), 0), FLT_ULP());
+
+               /* Imaginary axis */
+               z = CMPLXL(0.0, nums[i]);
+               test_odd_tol(csinh, z, CMPLXL(copysign(0, cos(nums[i])),
+                                                sin(nums[i])), DBL_ULP());
+               test_even_tol(ccosh, z, CMPLXL(cos(nums[i]),
+                   copysign(0, sin(nums[i]))), DBL_ULP());
+               test_odd_tol(ctanh, z, CMPLXL(0, tan(nums[i])), DBL_ULP());
+               test_odd_tol(csin, z, CMPLXL(0, sinh(nums[i])), DBL_ULP());
+               test_even_tol(ccos, z, CMPLXL(cosh(nums[i]), -0.0), DBL_ULP());
+               test_odd_tol(ctan, z, CMPLXL(0, tanh(nums[i])), DBL_ULP());
+
+               test_odd_tol(csinhf, z, CMPLXL(copysign(0, cosf(nums[i])),
+                                                sinf(nums[i])), FLT_ULP());
+               test_even_tol(ccoshf, z, CMPLXL(cosf(nums[i]),
+                   copysign(0, sinf(nums[i]))), FLT_ULP());
+               test_odd_tol(ctanhf, z, CMPLXL(0, tanf(nums[i])), FLT_ULP());
+               test_odd_tol(csinf, z, CMPLXL(0, sinhf(nums[i])), FLT_ULP());
+               test_even_tol(ccosf, z, CMPLXL(coshf(nums[i]), -0.0),
+                             FLT_ULP());
+               test_odd_tol(ctanf, z, CMPLXL(0, tanhf(nums[i])),
+                            1.3 * FLT_ULP());
+       }
+}
+
+ATF_TC(test_small_inputs);
+ATF_TC_HEAD(test_small_inputs, tc)
+{
+       atf_tc_set_md_var(tc, "descr", "test underflow inputs");
+}
+ATF_TC_BODY(test_small_inputs, tc)
+{
+       /*
+        * z =  0.5 + i Pi/4
+        *     sinh(z) = (sinh(0.5) + i cosh(0.5)) * sqrt(2)/2
+        *     cosh(z) = (cosh(0.5) + i sinh(0.5)) * sqrt(2)/2
+        *     tanh(z) = (2cosh(0.5)sinh(0.5) + i) / (2 cosh(0.5)**2 - 1)
+        * z = -0.5 + i Pi/2
+        *     sinh(z) = cosh(0.5)
+        *     cosh(z) = -i sinh(0.5)
+        *     tanh(z) = -coth(0.5)
+        * z =  1.0 + i 3Pi/4
+        *     sinh(z) = (-sinh(1) + i cosh(1)) * sqrt(2)/2
+        *     cosh(z) = (-cosh(1) + i sinh(1)) * sqrt(2)/2
+        *     tanh(z) = (2cosh(1)sinh(1) - i) / (2cosh(1)**2 - 1)
+        */
+       static const struct {
+               long double a, b;
+               long double sinh_a, sinh_b;
+               long double cosh_a, cosh_b;
+               long double tanh_a, tanh_b;
+       } tests[] = {
+               {  0.5L,
+                  0.78539816339744830961566084581987572L,
+                  0.36847002415910435172083660522240710L,
+                  0.79735196663945774996093142586179334L,
+                  0.79735196663945774996093142586179334L,
+                  0.36847002415910435172083660522240710L,
+                  0.76159415595576488811945828260479359L,
+                  0.64805427366388539957497735322615032L },
+               { -0.5L,
+                  1.57079632679489661923132169163975144L,
+                  0.0L,
+                  1.12762596520638078522622516140267201L,
+                  0.0L,
+                 -0.52109530549374736162242562641149156L,
+                 -2.16395341373865284877000401021802312L,
+                  0.0L },
+               {  1.0L,
+                  2.35619449019234492884698253745962716L,
+                 -0.83099273328405698212637979852748608L,
+                  1.09112278079550143030545602018565236L,
+                 -1.09112278079550143030545602018565236L,
+                  0.83099273328405698212637979852748609L,
+                  0.96402758007581688394641372410092315L,
+                 -0.26580222883407969212086273981988897L }
+       };
+       long double complex z;
+       unsigned i;
+
+       for (i = 0; i < nitems(tests); i++) {
+               z = CMPLXL(tests[i].a, tests[i].b);
+               testall_odd_tol(csinh, z,
+                   CMPLXL(tests[i].sinh_a, tests[i].sinh_b), 1.1);
+               testall_even_tol(ccosh, z,
+                   CMPLXL(tests[i].cosh_a, tests[i].cosh_b), 1.1);
+               testall_odd_tol(ctanh, z,
+                   CMPLXL(tests[i].tanh_a, tests[i].tanh_b), 1.4);
+        }
+}
+
+ATF_TC(test_large_inputs);
+ATF_TC_HEAD(test_large_inputs, tc)
+{
+       atf_tc_set_md_var(tc, "descr",
+           "Test inputs that might cause overflow in a sloppy implementation");
+}
+ATF_TC_BODY(test_large_inputs, tc)
+{
+       long double complex z;
+
+       /* tanh() uses a threshold around x=22, so check both sides. */
+       z = CMPLXL(21, 0.78539816339744830961566084581987572L);
+       testall_odd_tol(ctanh, z,
+           CMPLXL(1.0, 1.14990445285871196133287617611468468e-18L), 1.2);
+       z++;
+       testall_odd_tol(ctanh, z,
+           CMPLXL(1.0, 1.55622644822675930314266334585597964e-19L), 1);
+
+       z = CMPLXL(355, 0.78539816339744830961566084581987572L);
+       test_odd_tol(ctanh, z,
+                    CMPLXL(1.0, 8.95257245135025991216632140458264468e-309L),
+                    DBL_ULP());
+       z = CMPLXL(30, 0x1p1023L);
+       test_odd_tol(ctanh, z,
+                    CMPLXL(1.0, -1.62994325413993477997492170229268382e-26L),
+                    DBL_ULP());
+       z = CMPLXL(1, 0x1p1023L);
+       test_odd_tol(ctanh, z,
+                    CMPLXL(0.878606311888306869546254022621986509L,
+                           -0.225462792499754505792678258169527424L),
+                    DBL_ULP());
+
+       z = CMPLXL(710.6, 0.78539816339744830961566084581987572L);
+       test_odd_tol(csinh, z,
+           CMPLXL(1.43917579766621073533185387499658944e308L,
+                  1.43917579766621073533185387499658944e308L), DBL_ULP());
+       test_even_tol(ccosh, z,
+           CMPLXL(1.43917579766621073533185387499658944e308L,
+                  1.43917579766621073533185387499658944e308L), DBL_ULP());
+
+       z = CMPLXL(1500, 0.78539816339744830961566084581987572L);
+       testall_odd(csinh, z, CMPLXL(INFINITY, INFINITY), OPT_INEXACT,
+           FE_OVERFLOW, CS_BOTH);
+       testall_even(ccosh, z, CMPLXL(INFINITY, INFINITY), OPT_INEXACT,
+           FE_OVERFLOW, CS_BOTH);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+       ATF_TP_ADD_TC(tp, test_zero_input);
+       ATF_TP_ADD_TC(tp, test_nan_inputs);
+       ATF_TP_ADD_TC(tp, test_inf_inputs);
+       ATF_TP_ADD_TC(tp, test_axes);
+       ATF_TP_ADD_TC(tp, test_small_inputs);
+       ATF_TP_ADD_TC(tp, test_large_inputs);
+
+       return (atf_no_error());
+}
diff --git a/regress/lib/libm/msun/exponential_test.c b/regress/lib/libm/msun/exponential_test.c
new file mode 100644 (file)
index 0000000..c1b49ad
--- /dev/null
@@ -0,0 +1,197 @@
+/*     $OpenBSD: exponential_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $      */
+/*-
+ * Copyright (c) 2008 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Tests for corner cases in exp*().
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+#ifdef __i386__
+#include <ieeefp.h>
+#endif
+
+#include "test-utils.h"
+
+#pragma STDC FENV_ACCESS ON
+
+/*
+ * Test that a function returns the correct value and sets the
+ * exception flags correctly. The exceptmask specifies which
+ * exceptions we should check. We need to be lenient for several
+ * reasoons, but mainly because on some architectures it's impossible
+ * to raise FE_OVERFLOW without raising FE_INEXACT.
+ *
+ * These are macros instead of functions so that assert provides more
+ * meaningful error messages.
+ *
+ * XXX The volatile here is to avoid gcc's bogus constant folding and work
+ *     around the lack of support for the FENV_ACCESS pragma.
+ */
+#define        test(func, x, result, exceptmask, excepts)      do {            \
+       volatile long double _d = x;                                    \
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));                \
+       CHECK_FPEQUAL((func)(_d), (result));                    \
+       CHECK_FP_EXCEPTIONS_MSG(excepts, exceptmask, "for %s(%s)",      \
+           #func, #x);                                                 \
+} while (0)
+
+/* Test all the functions that compute b^x. */
+#define        _testall0(x, result, exceptmask, excepts)       do {            \
+       test(exp, x, result, exceptmask, excepts);                      \
+       test(expf, x, result, exceptmask, excepts);                     \
+       test(exp2, x, result, exceptmask, excepts);                     \
+       test(exp2f, x, result, exceptmask, excepts);                    \
+} while (0)
+
+/* Skip over exp2l on platforms that don't support it. */
+#if LDBL_PREC == 53
+#define        testall0        _testall0
+#else
+#define        testall0(x, result, exceptmask, excepts)        do {            \
+       _testall0(x, result, exceptmask, excepts);                      \
+       test(exp2l, x, result, exceptmask, excepts);                    \
+} while (0)
+#endif
+
+/* Test all the functions that compute b^x - 1. */
+#define        testall1(x, result, exceptmask, excepts)        do {            \
+       test(expm1, x, result, exceptmask, excepts);                    \
+       test(expm1f, x, result, exceptmask, excepts);                   \
+} while (0)
+
+static void
+run_generic_tests(void)
+{
+
+       /* exp(0) == 1, no exceptions raised */
+       testall0(0.0, 1.0, ALL_STD_EXCEPT, 0);
+       testall1(0.0, 0.0, ALL_STD_EXCEPT, 0);
+       testall0(-0.0, 1.0, ALL_STD_EXCEPT, 0);
+       testall1(-0.0, -0.0, ALL_STD_EXCEPT, 0);
+
+       /* exp(NaN) == NaN, no exceptions raised */
+       testall0(NAN, NAN, ALL_STD_EXCEPT, 0);
+       testall1(NAN, NAN, ALL_STD_EXCEPT, 0);
+
+       /* exp(Inf) == Inf, no exceptions raised */
+       testall0(INFINITY, INFINITY, ALL_STD_EXCEPT, 0);
+       testall1(INFINITY, INFINITY, ALL_STD_EXCEPT, 0);
+
+       /* exp(-Inf) == 0, no exceptions raised */
+       testall0(-INFINITY, 0.0, ALL_STD_EXCEPT, 0);
+       testall1(-INFINITY, -1.0, ALL_STD_EXCEPT, 0);
+
+#if !defined(__i386__)
+       /* exp(big) == Inf, overflow exception */
+       testall0(50000.0, INFINITY, ALL_STD_EXCEPT & ~FE_INEXACT, FE_OVERFLOW);
+       testall1(50000.0, INFINITY, ALL_STD_EXCEPT & ~FE_INEXACT, FE_OVERFLOW);
+
+       /* exp(small) == 0, underflow and inexact exceptions */
+       testall0(-50000.0, 0.0, ALL_STD_EXCEPT, FE_UNDERFLOW | FE_INEXACT);
+#endif
+       testall1(-50000.0, -1.0, ALL_STD_EXCEPT, FE_INEXACT);
+}
+
+
+/*
+ * We should insist that exp2() return exactly the correct
+ * result and not raise an inexact exception for integer
+ * arguments.
+ */
+ATF_TC_WITHOUT_HEAD(exp2f);
+ATF_TC_BODY(exp2f, tc)
+{
+       int i;
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+       for (i = FLT_MIN_EXP - FLT_MANT_DIG; i < FLT_MAX_EXP; i++) {
+               ATF_CHECK_EQ(exp2f(i), ldexpf(1.0, i));
+               CHECK_FP_EXCEPTIONS(0, ALL_STD_EXCEPT);
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(exp2);
+ATF_TC_BODY(exp2, tc)
+{
+       int i
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+       for (i = DBL_MIN_EXP - DBL_MANT_DIG; i < DBL_MAX_EXP; i++) {
+               ATF_CHECK_EQ(exp2(i), ldexp(1.0, i));
+               CHECK_FP_EXCEPTIONS(0, ALL_STD_EXCEPT);
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(exp2l);
+ATF_TC_BODY(exp2l, tc)
+{
+       int i;
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+       for (i = LDBL_MIN_EXP - LDBL_MANT_DIG; i < LDBL_MAX_EXP; i++) {
+               ATF_CHECK_EQ(exp2l(i), ldexpl(1.0, i));
+               CHECK_FP_EXCEPTIONS(0, ALL_STD_EXCEPT);
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(generic);
+ATF_TC_BODY(generic, tc)
+{
+       run_generic_tests();
+}
+
+#ifndef __OpenBSD__
+#ifdef __i386__
+ATF_TC_WITHOUT_HEAD(generic_fp_pe);
+ATF_TC_BODY(generic_fp_pe, tc)
+{
+       fpsetprec(FP_PE);
+       run_generic_tests();
+}
+#endif
+#endif
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, generic);
+#ifndef __OpenBSD__
+#ifdef __i386__
+       ATF_TP_ADD_TC(tp, generic_fp_pe);
+#endif
+#endif
+       ATF_TP_ADD_TC(tp, exp2);
+       ATF_TP_ADD_TC(tp, exp2f);
+       ATF_TP_ADD_TC(tp, exp2l);
+
+       return (atf_no_error());
+}
index 46f7406..529d8cc 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: fenv_test.c,v 1.5 2021/06/17 12:55:38 kettenis Exp $  */
+/*     $OpenBSD: fenv_test.c,v 1.6 2021/10/22 18:00:22 mbuhl Exp $     */
 /*-
  * Copyright (c) 2004 David Schultz <das@FreeBSD.org>
  * All rights reserved.
@@ -25,6 +25,8 @@
  * SUCH DAMAGE.
  */
 
+#include "macros.h"
+
 /*
  * Test the correctness and C99-compliance of various fenv.h features.
  */
 #include <err.h>
 #include <fenv.h>
 #include <float.h>
+#ifndef __OpenBSD__
+#include <libutil.h>
+#endif
 #include <math.h>
 #include <signal.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
-/*
- * Implementations are permitted to define additional exception flags
- * not specified in the standard, so it is not necessarily true that
- * FE_ALL_EXCEPT == ALL_STD_EXCEPT.
- */
-#define        ALL_STD_EXCEPT  (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \
-                        FE_OVERFLOW | FE_UNDERFLOW)
+#include "test-utils.h"
 
 #define        NEXCEPTS        (sizeof(std_excepts) / sizeof(std_excepts[0]))
 
@@ -70,11 +69,14 @@ static int std_except_sets[1 << NEXCEPTS];
 /*
  * Initialize std_except_sets[] to the power set of std_excepts[]
  */
-static void
-init_exceptsets(void)
+static __attribute__((constructor)) void
+do_setup(void)
 {
        unsigned i, j, sr;
 
+       /* Avoid double output after fork() */
+       setvbuf(stdout, NULL, _IONBF, 0);
+
        for (i = 0; i < 1 << NEXCEPTS; i++) {
                for (sr = i, j = 0; sr != 0; sr >>= 1, j++)
                        std_except_sets[i] |= std_excepts[j] & ((~sr & 1) - 1);
@@ -165,7 +167,7 @@ static void
 trap_handler(int sig)
 {
 
-       assert(sig == SIGFPE);
+       ATF_CHECK_EQ(SIGFPE, sig);
        _exit(0);
 }
 
@@ -174,14 +176,17 @@ trap_handler(int sig)
  * The memcmp() test below may be too much to ask for, since there
  * could be multiple machine-specific default environments.
  */
-static void
-test_dfl_env(void)
+ATF_TC_WITHOUT_HEAD(dfl_env);
+ATF_TC_BODY(dfl_env, tc)
 {
 #ifndef NO_STRICT_DFL_ENV
        fenv_t env;
 
        fegetenv(&env);
-
+       /* Print the default environment for debugging purposes. */
+       hexdump(&env, sizeof(env), "current fenv ", HD_OMIT_CHARS);
+       hexdump(FE_DFL_ENV, sizeof(env), "default fenv ", HD_OMIT_CHARS);
+       CHECK_FP_EXCEPTIONS(0, FE_ALL_EXCEPT);
 #ifdef __amd64__
        /*
         * Compare the fields that the AMD [1] and Intel [2] specs say will be
@@ -197,52 +202,51 @@ test_dfl_env(void)
         * 1. http://support.amd.com/TechDocs/26569_APM_v5.pdf
         * 2. http://www.intel.com/Assets/en_US/PDF/manual/253666.pdf
         */
-       assert(memcmp(&env.__mxcsr, &FE_DFL_ENV->__mxcsr,
+       ATF_CHECK(memcmp(&env.__mxcsr, &FE_DFL_ENV->__mxcsr,
            sizeof(env.__mxcsr)) == 0);
-       assert(memcmp(&env.__x87.__control, &FE_DFL_ENV->__x87.__control,
+       ATF_CHECK(memcmp(&env.__x87.__control, &FE_DFL_ENV->__x87.__control,
            sizeof(env.__x87.__control)) == 0);
-       assert(memcmp(&env.__x87.__status, &FE_DFL_ENV->__x87.__status,
+       ATF_CHECK(memcmp(&env.__x87.__status, &FE_DFL_ENV->__x87.__status,
            sizeof(env.__x87.__status)) == 0);
-       assert(memcmp(&env.__x87.__tag, &FE_DFL_ENV->__x87.__tag,
+       ATF_CHECK(memcmp(&env.__x87.__tag, &FE_DFL_ENV->__x87.__tag,
            sizeof(env.__x87.__tag)) == 0);
 #else
-       assert(memcmp(&env, FE_DFL_ENV, sizeof(env)) == 0);
+       ATF_CHECK_EQ(0, memcmp(&env, FE_DFL_ENV, sizeof(env)));
 #endif
 
 #endif
-       assert(fetestexcept(FE_ALL_EXCEPT) == 0);
+       CHECK_FP_EXCEPTIONS(0, FE_ALL_EXCEPT);
 }
 
 /*
  * Test fetestexcept() and feclearexcept().
  */
-static void
-test_fetestclearexcept(void)
+ATF_TC_WITHOUT_HEAD(fetestclearexcept);
+ATF_TC_BODY(fetestclearexcept, tc)
 {
        int excepts, i;
 
        for (i = 0; i < 1 << NEXCEPTS; i++)
-               assert(fetestexcept(std_except_sets[i]) == 0);
+               ATF_CHECK_EQ(0, fetestexcept(std_except_sets[i]));
        for (i = 0; i < 1 << NEXCEPTS; i++) {
                excepts = std_except_sets[i];
 
                /* FE_ALL_EXCEPT might be special-cased, as on i386. */
                raiseexcept(excepts);
-               assert(fetestexcept(excepts) == excepts);
-               assert(feclearexcept(FE_ALL_EXCEPT) == 0);
-               assert(fetestexcept(FE_ALL_EXCEPT) == 0);
+               ATF_CHECK_EQ(excepts, fetestexcept(excepts));
+               ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+               ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
 
                raiseexcept(excepts);
-               assert(fetestexcept(excepts) == excepts);
+               ATF_CHECK_EQ(excepts, fetestexcept(excepts));
                if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
                        excepts |= FE_INEXACT;
-                       assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) ==
-                           excepts);
+                       ATF_CHECK_EQ(excepts, (fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT));
                } else {
-                       assert(fetestexcept(ALL_STD_EXCEPT) == excepts);
+                       ATF_CHECK_EQ(excepts, fetestexcept(ALL_STD_EXCEPT));
                }
-               assert(feclearexcept(excepts) == 0);
-               assert(fetestexcept(ALL_STD_EXCEPT) == 0);
+               ATF_CHECK_EQ(0, feclearexcept(excepts));
+               ATF_CHECK_EQ(0, fetestexcept(ALL_STD_EXCEPT));
        }
 }
 
@@ -251,31 +255,29 @@ test_fetestclearexcept(void)
  *
  * Prerequisites: fetestexcept(), feclearexcept()
  */
-static void
-test_fegsetexceptflag(void)
+ATF_TC_WITHOUT_HEAD(fegsetexceptflag);
+ATF_TC_BODY(fegsetexceptflag, tc)
 {
        fexcept_t flag;
        int excepts, i;
 
-       assert(fetestexcept(FE_ALL_EXCEPT) == 0);
+       CHECK_FP_EXCEPTIONS(0, FE_ALL_EXCEPT);
        for (i = 0; i < 1 << NEXCEPTS; i++) {
                excepts = std_except_sets[i];
 
-               assert(fegetexceptflag(&flag, excepts) == 0);
+               ATF_CHECK_EQ(0, fegetexceptflag(&flag, excepts));
                raiseexcept(ALL_STD_EXCEPT);
-               assert(fesetexceptflag(&flag, excepts) == 0);
-               assert(fetestexcept(ALL_STD_EXCEPT) ==
-                   (ALL_STD_EXCEPT ^ excepts));
-
-               assert(fegetexceptflag(&flag, FE_ALL_EXCEPT) == 0);
-               assert(feclearexcept(FE_ALL_EXCEPT) == 0);
-               assert(fesetexceptflag(&flag, excepts) == 0);
-               assert(fetestexcept(ALL_STD_EXCEPT) == 0);
-               assert(fesetexceptflag(&flag, ALL_STD_EXCEPT ^ excepts) == 0);
-               assert(fetestexcept(ALL_STD_EXCEPT) ==
-                   (ALL_STD_EXCEPT ^ excepts));
-
-               assert(feclearexcept(FE_ALL_EXCEPT) == 0);
+               ATF_CHECK_EQ(0, fesetexceptflag(&flag, excepts));
+               ATF_CHECK_EQ((ALL_STD_EXCEPT ^ excepts), fetestexcept(ALL_STD_EXCEPT));
+
+               ATF_CHECK_EQ(0, fegetexceptflag(&flag, FE_ALL_EXCEPT));
+               ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+               ATF_CHECK_EQ(0, fesetexceptflag(&flag, excepts));
+               ATF_CHECK_EQ(0, fetestexcept(ALL_STD_EXCEPT));
+               ATF_CHECK_EQ(0, fesetexceptflag(&flag, ALL_STD_EXCEPT ^ excepts));
+               ATF_CHECK_EQ((ALL_STD_EXCEPT ^ excepts), fetestexcept(ALL_STD_EXCEPT));
+
+               ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
        }
 }
 
@@ -284,63 +286,62 @@ test_fegsetexceptflag(void)
  *
  * Prerequisites: fetestexcept(), feclearexcept()
  */
-static void
-test_feraiseexcept(void)
+ATF_TC_WITHOUT_HEAD(feraiseexcept);
+ATF_TC_BODY(feraiseexcept, tc)
 {
        int excepts, i;
 
        for (i = 0; i < 1 << NEXCEPTS; i++) {
                excepts = std_except_sets[i];
 
-               assert(fetestexcept(FE_ALL_EXCEPT) == 0);
-               assert(feraiseexcept(excepts) == 0);
+               ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
+               ATF_CHECK_EQ(0, feraiseexcept(excepts));
                if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
                        excepts |= FE_INEXACT;
-                       assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) ==
-                           excepts);
+                       ATF_CHECK_EQ(excepts, (fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT));
                } else {
-                       assert(fetestexcept(ALL_STD_EXCEPT) == excepts);
+                       ATF_CHECK_EQ(excepts, fetestexcept(ALL_STD_EXCEPT));
                }
-               assert(feclearexcept(FE_ALL_EXCEPT) == 0);
+               ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
        }
-       assert(feraiseexcept(FE_INVALID | FE_DIVBYZERO) == 0);
-       assert(fetestexcept(ALL_STD_EXCEPT) == (FE_INVALID | FE_DIVBYZERO));
-       assert(feraiseexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) == 0);
-       assert(fetestexcept(ALL_STD_EXCEPT) == ALL_STD_EXCEPT);
-       assert(feclearexcept(FE_ALL_EXCEPT) == 0);
+       ATF_CHECK_EQ(0, feraiseexcept(FE_INVALID | FE_DIVBYZERO));
+       ATF_CHECK_EQ((FE_INVALID | FE_DIVBYZERO), fetestexcept(ALL_STD_EXCEPT));
+       ATF_CHECK_EQ(0, feraiseexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT));
+       ATF_CHECK_EQ(ALL_STD_EXCEPT, fetestexcept(ALL_STD_EXCEPT));
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
 }
 
 /*
  * Test fegetround() and fesetround().
  */
-static void
-test_fegsetround(void)
+ATF_TC_WITHOUT_HEAD(fegsetround);
+ATF_TC_BODY(fegsetround, tc)
 {
 
-       assert(fegetround() == FE_TONEAREST);
-       assert(getround() == FE_TONEAREST);
-       assert(FLT_ROUNDS == 1);
+       ATF_CHECK_EQ(FE_TONEAREST, fegetround());
+       ATF_CHECK_EQ(FE_TONEAREST, getround());
+       ATF_CHECK_EQ(1, FLT_ROUNDS);
 
-       assert(fesetround(FE_DOWNWARD) == 0);
-       assert(fegetround() == FE_DOWNWARD);
-       assert(getround() == FE_DOWNWARD);
-       assert(FLT_ROUNDS == 3);
+       ATF_CHECK_EQ(0, fesetround(FE_DOWNWARD));
+       ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
+       ATF_CHECK_EQ(FE_DOWNWARD, getround());
+       ATF_CHECK_EQ(3, FLT_ROUNDS);
 
-       assert(fesetround(FE_UPWARD) == 0);
-       assert(getround() == FE_UPWARD);
-       assert(fegetround() == FE_UPWARD);
-       assert(FLT_ROUNDS == 2);
+       ATF_CHECK_EQ(0, fesetround(FE_UPWARD));
+       ATF_CHECK_EQ(FE_UPWARD, getround());
+       ATF_CHECK_EQ(FE_UPWARD, fegetround());
+       ATF_CHECK_EQ(2, FLT_ROUNDS);
 
-       assert(fesetround(FE_TOWARDZERO) == 0);
-       assert(getround() == FE_TOWARDZERO);
-       assert(fegetround() == FE_TOWARDZERO);
-       assert(FLT_ROUNDS == 0);
+       ATF_CHECK_EQ(0, fesetround(FE_TOWARDZERO));
+       ATF_CHECK_EQ(FE_TOWARDZERO, getround());
+       ATF_CHECK_EQ(FE_TOWARDZERO, fegetround());
+       ATF_CHECK_EQ(0, FLT_ROUNDS);
 
-       assert(fesetround(FE_TONEAREST) == 0);
-       assert(getround() == FE_TONEAREST);
-       assert(FLT_ROUNDS == 1);
+       ATF_CHECK_EQ(0, fesetround(FE_TONEAREST));
+       ATF_CHECK_EQ(FE_TONEAREST, getround());
+       ATF_CHECK_EQ(1, FLT_ROUNDS);
 
-       assert(feclearexcept(FE_ALL_EXCEPT) == 0);
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
 }
 
 /*
@@ -348,8 +349,8 @@ test_fegsetround(void)
  *
  * Prerequisites: fetestexcept(), feclearexcept(), fegetround(), fesetround()
  */
-static void
-test_fegsetenv(void)
+ATF_TC_WITHOUT_HEAD(fegsetenv);
+ATF_TC_BODY(fegsetenv, tc)
 {
        fenv_t env1, env2;
        int excepts, i;
@@ -357,9 +358,9 @@ test_fegsetenv(void)
        for (i = 0; i < 1 << NEXCEPTS; i++) {
                excepts = std_except_sets[i];
 
-               assert(fetestexcept(FE_ALL_EXCEPT) == 0);
-               assert(fegetround() == FE_TONEAREST);
-               assert(fegetenv(&env1) == 0);
+               ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
+               ATF_CHECK_EQ(FE_TONEAREST, fegetround());
+               ATF_CHECK_EQ(0, fegetenv(&env1));
 
                /*
                 * fe[gs]etenv() should be able to save and restore
@@ -369,20 +370,26 @@ test_fegsetenv(void)
                raiseexcept(excepts);
                if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0 &&
                    (excepts & FE_INEXACT) == 0)
-                       assert(feclearexcept(FE_INEXACT) == 0);
+                       ATF_CHECK_EQ(0, feclearexcept(FE_INEXACT));
 
                fesetround(FE_DOWNWARD);
-               assert(fegetenv(&env2) == 0);
-               assert(fesetenv(&env1) == 0);
-               assert(fetestexcept(FE_ALL_EXCEPT) == 0);
-               assert(fegetround() == FE_TONEAREST);
-
-               assert(fesetenv(&env2) == 0);
-               assert(fetestexcept(FE_ALL_EXCEPT) == excepts);
-               assert(fegetround() == FE_DOWNWARD);
-               assert(fesetenv(&env1) == 0);
-               assert(fetestexcept(FE_ALL_EXCEPT) == 0);
-               assert(fegetround() == FE_TONEAREST);
+               ATF_CHECK_EQ(0, fegetenv(&env2));
+               ATF_CHECK_EQ(0, fesetenv(&env1));
+               ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
+               ATF_CHECK_EQ(FE_TONEAREST, fegetround());
+
+               ATF_CHECK_EQ(0, fesetenv(&env2));
+
+               /* 
+                * Some platforms like powerpc may set extra exception bits. Since
+                * only standard exceptions are tested, mask against ALL_STD_EXCEPT 
+                */
+               ATF_CHECK_EQ(excepts, (fetestexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT));
+
+               ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
+               ATF_CHECK_EQ(0, fesetenv(&env1));
+               ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
+               ATF_CHECK_EQ(FE_TONEAREST, fegetround());
        }
 }
 
@@ -391,24 +398,41 @@ test_fegsetenv(void)
  *
  * Prerequisites: fetestexcept(), feraiseexcept()
  */
-static void
-test_masking(void)
+ATF_TC_WITHOUT_HEAD(masking);
+ATF_TC_BODY(masking, tc)
 {
 #if !defined(__arm__) && !defined(__aarch64__) && !defined(__riscv)
        struct sigaction act;
        int except, pass, raise, status;
        unsigned i;
 
-       assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
-       assert((feenableexcept(FE_INVALID|FE_OVERFLOW) & ALL_STD_EXCEPT) == 0);
-       assert((feenableexcept(FE_UNDERFLOW) & ALL_STD_EXCEPT) ==
-           (FE_INVALID | FE_OVERFLOW));
-       assert((fedisableexcept(FE_OVERFLOW) & ALL_STD_EXCEPT) ==
-           (FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW));
-       assert((fegetexcept() & ALL_STD_EXCEPT) == (FE_INVALID | FE_UNDERFLOW));
-       assert((fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT) ==
-           (FE_INVALID | FE_UNDERFLOW));
-       assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
+       ATF_REQUIRE_EQ(0, (fegetexcept() & ALL_STD_EXCEPT));
+
+       /*
+        * Some CPUs, e.g. AArch64 QEMU does not support trapping on FP
+        * exceptions. In that case the trap enable bits are all RAZ/WI, so
+        * writing to those bits will be ignored and the the next read will
+        * return all zeroes for those bits. Skip the test if no floating
+        * point exceptions are supported and mark it XFAIL if some are missing.
+        */
+       ATF_REQUIRE_EQ(0, (feenableexcept(FE_ALL_EXCEPT)));
+       except = fegetexcept();
+       if (except == 0) {
+               atf_tc_skip("CPU does not support trapping on floating point "
+                   "exceptions.");
+       } else if ((except & ALL_STD_EXCEPT) != ALL_STD_EXCEPT) {
+               atf_tc_expect_fail("Not all floating point exceptions can be "
+                   "set to trap: %#x vs %#x", except, ALL_STD_EXCEPT);
+       }
+       fedisableexcept(FE_ALL_EXCEPT);
+
+
+       ATF_CHECK_EQ(0, (feenableexcept(FE_INVALID|FE_OVERFLOW) & ALL_STD_EXCEPT));
+       ATF_CHECK_EQ((FE_INVALID | FE_OVERFLOW), (feenableexcept(FE_UNDERFLOW) & ALL_STD_EXCEPT));
+       ATF_CHECK_EQ((FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW), (fedisableexcept(FE_OVERFLOW) & ALL_STD_EXCEPT));
+       ATF_CHECK_EQ((FE_INVALID | FE_UNDERFLOW), (fegetexcept() & ALL_STD_EXCEPT));
+       ATF_CHECK_EQ((FE_INVALID | FE_UNDERFLOW), (fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT));
+       ATF_CHECK_EQ(0, (fegetexcept() & ALL_STD_EXCEPT));
 
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
@@ -429,40 +453,39 @@ test_masking(void)
                         */
                        switch(fork()) {
                        case 0:         /* child */
-                               assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
-                               assert((feenableexcept(except)
-                                          & ALL_STD_EXCEPT) == 0);
-                               assert(fegetexcept() == except);
+                               ATF_CHECK_EQ(0, (fegetexcept() & ALL_STD_EXCEPT));
+                               ATF_REQUIRE_EQ(0, (feenableexcept(except) & ALL_STD_EXCEPT));
+                               ATF_CHECK_EQ(except, fegetexcept());
                                raiseexcept(raise);
-                               assert(feraiseexcept(raise) == 0);
-                               assert(fetestexcept(ALL_STD_EXCEPT) == raise);
-
-                               assert(sigaction(SIGFPE, &act, NULL) == 0);
+                               ATF_CHECK_EQ(0, feraiseexcept(raise));
+                               ATF_CHECK_EQ(raise, fetestexcept(ALL_STD_EXCEPT));
+                               ATF_CHECK_EQ(0, sigaction(SIGFPE, &act, NULL));
                                switch (pass) {
                                case 0:
                                        raiseexcept(except);
                                case 1:
                                        feraiseexcept(except);
                                default:
-                                       assert(0);
+                                       ATF_REQUIRE(0);
                                }
-                               assert(0);
+                               ATF_REQUIRE(0);
                        default:        /* parent */
-                               assert(wait(&status) > 0);
+                               ATF_REQUIRE(wait(&status) > 0);
                                /*
                                 * Avoid assert() here so that it's possible
                                 * to examine a failed child's core dump.
                                 */
                                if (!WIFEXITED(status))
                                        errx(1, "child aborted\n");
-                               assert(WEXITSTATUS(status) == 0);
+                               ATF_CHECK_EQ(0, WEXITSTATUS(status));
                                break;
                        case -1:        /* error */
-                               assert(0);
+                               ATF_REQUIRE(0);
                        }
                }
        }
-       assert(fetestexcept(FE_ALL_EXCEPT) == 0);
+       ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
 #endif
 }
 
@@ -472,8 +495,8 @@ test_masking(void)
  * Prerequisites: fetestexcept(), fegetround(), fesetround(),
  *     fedisableexcept(), feenableexcept()
  */
-static void
-test_feholdupdate(void)
+ATF_TC_WITHOUT_HEAD(feholdupdate);
+ATF_TC_BODY(feholdupdate, tc)
 {
        fenv_t env;
 
@@ -506,68 +529,55 @@ test_feholdupdate(void)
                                 * check other properties of feupdateenv().
                                 */
                                if (pass == 1)
-                                       assert((feenableexcept(except) &
-                                                  ALL_STD_EXCEPT) == 0);
+                                       ATF_REQUIRE_EQ(0,
+                                           feenableexcept(except) &
+                                           ALL_STD_EXCEPT);
                                raiseexcept(raise);
-                               assert(fesetround(FE_DOWNWARD) == 0);
-                               assert(feholdexcept(&env) == 0);
-                               assert(fetestexcept(FE_ALL_EXCEPT) == 0);
+                               ATF_CHECK_EQ(0, fesetround(FE_DOWNWARD));
+                               ATF_CHECK_EQ(0, feholdexcept(&env));
+                               ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
                                raiseexcept(except);
-                               assert(fesetround(FE_UPWARD) == 0);
+                               ATF_CHECK_EQ(0, fesetround(FE_UPWARD));
 
                                if (pass == 1)
-                                       assert(sigaction(SIGFPE, &act, NULL) ==
-                                           0);
-                               assert(feupdateenv(&env) == 0);
-                               assert(fegetround() == FE_DOWNWARD);
-                               assert(fetestexcept(ALL_STD_EXCEPT) ==
-                                   (except | raise));
-
-                               assert(pass == 0);
+                                       ATF_CHECK_EQ(0, sigaction(SIGFPE, &act, NULL));
+                               ATF_CHECK_EQ(0, feupdateenv(&env));
+                               ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
+                               ATF_CHECK_EQ((except | raise), fetestexcept(ALL_STD_EXCEPT));
+
+                               ATF_CHECK_EQ(0, pass);
                                _exit(0);
                        default:        /* parent */
-                               assert(wait(&status) > 0);
+                               ATF_REQUIRE(wait(&status) > 0);
                                /*
                                 * Avoid assert() here so that it's possible
                                 * to examine a failed child's core dump.
                                 */
                                if (!WIFEXITED(status))
                                        errx(1, "child aborted\n");
-                               assert(WEXITSTATUS(status) == 0);
+                               ATF_CHECK_EQ(0, WEXITSTATUS(status));
                                break;
                        case -1:        /* error */
-                               assert(0);
+                               ATF_REQUIRE(0);
                        }
                }
 #if defined(__arm__) || defined(__aarch64__) || defined(__riscv)
                break;
 #endif
        }
-       assert(fetestexcept(FE_ALL_EXCEPT) == 0);
+       ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
 }
 
-int
-main(void)
+ATF_TP_ADD_TCS(tp)
 {
-
-       printf("1..8\n");
-       init_exceptsets();
-       test_dfl_env();
-       printf("ok 1 - fenv\n");
-       test_fetestclearexcept();
-       printf("ok 2 - fenv\n");
-       test_fegsetexceptflag();
-       printf("ok 3 - fenv\n");
-       test_feraiseexcept();
-       printf("ok 4 - fenv\n");
-       test_fegsetround();
-       printf("ok 5 - fenv\n");
-       test_fegsetenv();
-       printf("ok 6 - fenv\n");
-       test_masking();
-       printf("ok 7 - fenv\n");
-       test_feholdupdate();
-       printf("ok 8 - fenv\n");
-
-       return (0);
+       ATF_TP_ADD_TC(tp, dfl_env);
+       ATF_TP_ADD_TC(tp, fetestclearexcept);
+       ATF_TP_ADD_TC(tp, fegsetexceptflag);
+       ATF_TP_ADD_TC(tp, feraiseexcept);
+       ATF_TP_ADD_TC(tp, fegsetround);
+       ATF_TP_ADD_TC(tp, fegsetenv);
+       ATF_TP_ADD_TC(tp, masking);
+       ATF_TP_ADD_TC(tp, feholdupdate);
+
+       return (atf_no_error());
 }
diff --git a/regress/lib/libm/msun/fls.c b/regress/lib/libm/msun/fls.c
new file mode 100644 (file)
index 0000000..f588acf
--- /dev/null
@@ -0,0 +1,53 @@
+/*     $OpenBSD: fls.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $   */
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <strings.h>
+
+/*
+ * Find Last Set bit
+ */
+int
+fls(int mask)
+{
+       int bit;
+
+       if (mask == 0)
+               return (0);
+       for (bit = 1; mask != 1; bit++)
+               mask = (unsigned int)mask >> 1;
+       return (bit);
+}
diff --git a/regress/lib/libm/msun/fma_test.c b/regress/lib/libm/msun/fma_test.c
new file mode 100644 (file)
index 0000000..ce1dfee
--- /dev/null
@@ -0,0 +1,557 @@
+/*     $OpenBSD: fma_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $      */
+/*-
+ * Copyright (c) 2008 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Tests for fma{,f,l}().
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test-utils.h"
+
+#pragma STDC FENV_ACCESS ON
+
+/*
+ * Test that a function returns the correct value and sets the
+ * exception flags correctly. The exceptmask specifies which
+ * exceptions we should check. We need to be lenient for several
+ * reasons, but mainly because on some architectures it's impossible
+ * to raise FE_OVERFLOW without raising FE_INEXACT.
+ *
+ * These are macros instead of functions so that assert provides more
+ * meaningful error messages.
+ */
+#define        test(func, x, y, z, result, exceptmask, excepts) do {           \
+       volatile long double _vx = (x), _vy = (y), _vz = (z);           \
+       ATF_CHECK(feclearexcept(FE_ALL_EXCEPT) == 0);                   \
+       CHECK_FPEQUAL((func)(_vx, _vy, _vz), (result));         \
+       CHECK_FP_EXCEPTIONS_MSG(excepts, exceptmask, "for %s(%s)",      \
+           #func, #x);                                                 \
+} while (0)
+
+#define        testall(x, y, z, result, exceptmask, excepts)   do {            \
+       test(fma, (double)(x), (double)(y), (double)(z),                \
+               (double)(result), (exceptmask), (excepts));             \
+       test(fmaf, (float)(x), (float)(y), (float)(z),                  \
+               (float)(result), (exceptmask), (excepts));              \
+       test(fmal, (x), (y), (z), (result), (exceptmask), (excepts));   \
+} while (0)
+
+/* Test in all rounding modes. */
+#define        testrnd(func, x, y, z, rn, ru, rd, rz, exceptmask, excepts)     do { \
+       fesetround(FE_TONEAREST);                                       \
+       test((func), (x), (y), (z), (rn), (exceptmask), (excepts));     \
+       fesetround(FE_UPWARD);                                          \
+       test((func), (x), (y), (z), (ru), (exceptmask), (excepts));     \
+       fesetround(FE_DOWNWARD);                                        \
+       test((func), (x), (y), (z), (rd), (exceptmask), (excepts));     \
+       fesetround(FE_TOWARDZERO);                                      \
+       test((func), (x), (y), (z), (rz), (exceptmask), (excepts));     \
+} while (0)
+
+/*
+ * This is needed because clang constant-folds fma in ways that are incorrect
+ * in rounding modes other than FE_TONEAREST.
+ */
+static volatile double one = 1.0;
+
+static void
+test_zeroes(void)
+{
+       const int rd = (fegetround() == FE_DOWNWARD);
+
+       testall(0.0, 0.0, 0.0, 0.0, ALL_STD_EXCEPT, 0);
+       testall(1.0, 0.0, 0.0, 0.0, ALL_STD_EXCEPT, 0);
+       testall(0.0, 1.0, 0.0, 0.0, ALL_STD_EXCEPT, 0);
+       testall(0.0, 0.0, 1.0, 1.0, ALL_STD_EXCEPT, 0);
+
+       testall(-0.0, 0.0, 0.0, rd ? -0.0 : 0.0, ALL_STD_EXCEPT, 0);
+       testall(0.0, -0.0, 0.0, rd ? -0.0 : 0.0, ALL_STD_EXCEPT, 0);
+       testall(-0.0, -0.0, 0.0, 0.0, ALL_STD_EXCEPT, 0);
+       testall(0.0, 0.0, -0.0, rd ? -0.0 : 0.0, ALL_STD_EXCEPT, 0);
+       testall(-0.0, -0.0, -0.0, rd ? -0.0 : 0.0, ALL_STD_EXCEPT, 0);
+
+       testall(-0.0, 0.0, -0.0, -0.0, ALL_STD_EXCEPT, 0);
+       testall(0.0, -0.0, -0.0, -0.0, ALL_STD_EXCEPT, 0);
+
+       testall(-one, one, one, rd ? -0.0 : 0.0, ALL_STD_EXCEPT, 0);
+       testall(one, -one, one, rd ? -0.0 : 0.0, ALL_STD_EXCEPT, 0);
+       testall(-one, -one, -one, rd ? -0.0 : 0.0, ALL_STD_EXCEPT, 0);
+
+       switch (fegetround()) {
+       case FE_TONEAREST:
+       case FE_TOWARDZERO:
+               test(fmaf, -FLT_MIN, FLT_MIN, 0.0, -0.0,
+                    ALL_STD_EXCEPT, FE_INEXACT | FE_UNDERFLOW);
+               test(fma, -DBL_MIN, DBL_MIN, 0.0, -0.0,
+                    ALL_STD_EXCEPT, FE_INEXACT | FE_UNDERFLOW);
+               test(fmal, -LDBL_MIN, LDBL_MIN, 0.0, -0.0,
+                    ALL_STD_EXCEPT, FE_INEXACT | FE_UNDERFLOW);
+       }
+}
+
+static void
+test_infinities(void)
+{
+       testall(INFINITY, 1.0, -1.0, INFINITY, ALL_STD_EXCEPT, 0);
+       testall(-1.0, INFINITY, 0.0, -INFINITY, ALL_STD_EXCEPT, 0);
+       testall(0.0, 0.0, INFINITY, INFINITY, ALL_STD_EXCEPT, 0);
+       testall(1.0, 1.0, INFINITY, INFINITY, ALL_STD_EXCEPT, 0);
+       testall(1.0, 1.0, -INFINITY, -INFINITY, ALL_STD_EXCEPT, 0);
+
+       testall(INFINITY, -INFINITY, 1.0, -INFINITY, ALL_STD_EXCEPT, 0);
+       testall(INFINITY, INFINITY, 1.0, INFINITY, ALL_STD_EXCEPT, 0);
+       testall(-INFINITY, -INFINITY, INFINITY, INFINITY, ALL_STD_EXCEPT, 0);
+
+       testall(0.0, INFINITY, 1.0, NAN, ALL_STD_EXCEPT, FE_INVALID);
+       testall(INFINITY, 0.0, -0.0, NAN, ALL_STD_EXCEPT, FE_INVALID);
+
+       /* The invalid exception is optional in this case. */
+       testall(INFINITY, 0.0, NAN, NAN, ALL_STD_EXCEPT & ~FE_INVALID, 0);
+
+       testall(INFINITY, INFINITY, -INFINITY, NAN,
+               ALL_STD_EXCEPT, FE_INVALID);
+       testall(-INFINITY, INFINITY, INFINITY, NAN,
+               ALL_STD_EXCEPT, FE_INVALID);
+       testall(INFINITY, -1.0, INFINITY, NAN,
+               ALL_STD_EXCEPT, FE_INVALID);
+
+       test(fmaf, FLT_MAX, FLT_MAX, -INFINITY, -INFINITY, ALL_STD_EXCEPT, 0);
+       test(fma, DBL_MAX, DBL_MAX, -INFINITY, -INFINITY, ALL_STD_EXCEPT, 0);
+       test(fmal, LDBL_MAX, LDBL_MAX, -INFINITY, -INFINITY,
+            ALL_STD_EXCEPT, 0);
+       test(fmaf, FLT_MAX, -FLT_MAX, INFINITY, INFINITY, ALL_STD_EXCEPT, 0);
+       test(fma, DBL_MAX, -DBL_MAX, INFINITY, INFINITY, ALL_STD_EXCEPT, 0);
+       test(fmal, LDBL_MAX, -LDBL_MAX, INFINITY, INFINITY,
+            ALL_STD_EXCEPT, 0);
+}
+
+static void
+test_nans(void)
+{
+       testall(NAN, 0.0, 0.0, NAN, ALL_STD_EXCEPT, 0);
+       testall(1.0, NAN, 1.0, NAN, ALL_STD_EXCEPT, 0);
+       testall(1.0, -1.0, NAN, NAN, ALL_STD_EXCEPT, 0);
+       testall(0.0, 0.0, NAN, NAN, ALL_STD_EXCEPT, 0);
+       testall(NAN, NAN, NAN, NAN, ALL_STD_EXCEPT, 0);
+
+       /* x*y should not raise an inexact/overflow/underflow if z is NaN. */
+       testall(M_PI, M_PI, NAN, NAN, ALL_STD_EXCEPT, 0);
+       test(fmaf, FLT_MIN, FLT_MIN, NAN, NAN, ALL_STD_EXCEPT, 0);
+       test(fma, DBL_MIN, DBL_MIN, NAN, NAN, ALL_STD_EXCEPT, 0);
+       test(fmal, LDBL_MIN, LDBL_MIN, NAN, NAN, ALL_STD_EXCEPT, 0);
+       test(fmaf, FLT_MAX, FLT_MAX, NAN, NAN, ALL_STD_EXCEPT, 0);
+       test(fma, DBL_MAX, DBL_MAX, NAN, NAN, ALL_STD_EXCEPT, 0);
+       test(fmal, LDBL_MAX, LDBL_MAX, NAN, NAN, ALL_STD_EXCEPT, 0);
+}
+
+/*
+ * Tests for cases where z is very small compared to x*y.
+ */
+static void
+test_small_z(void)
+{
+       /* x*y positive, z positive */
+       if (fegetround() == FE_UPWARD) {
+               test(fmaf, one, one, 0x1.0p-100, 1.0 + FLT_EPSILON,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fma, one, one, 0x1.0p-200, 1.0 + DBL_EPSILON,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fmal, one, one, 0x1.0p-200, 1.0 + LDBL_EPSILON,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+       } else {
+               testall(0x1.0p100, one, 0x1.0p-100, 0x1.0p100,
+                       ALL_STD_EXCEPT, FE_INEXACT);
+       }
+
+       /* x*y negative, z negative */
+       if (fegetround() == FE_DOWNWARD) {
+               test(fmaf, -one, one, -0x1.0p-100, -(1.0 + FLT_EPSILON),
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fma, -one, one, -0x1.0p-200, -(1.0 + DBL_EPSILON),
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fmal, -one, one, -0x1.0p-200, -(1.0 + LDBL_EPSILON),
+                    ALL_STD_EXCEPT, FE_INEXACT);
+       } else {
+               testall(0x1.0p100, -one, -0x1.0p-100, -0x1.0p100,
+                       ALL_STD_EXCEPT, FE_INEXACT);
+       }
+
+       /* x*y positive, z negative */
+       if (fegetround() == FE_DOWNWARD || fegetround() == FE_TOWARDZERO) {
+               test(fmaf, one, one, -0x1.0p-100, 1.0 - FLT_EPSILON / 2,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fma, one, one, -0x1.0p-200, 1.0 - DBL_EPSILON / 2,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fmal, one, one, -0x1.0p-200, 1.0 - LDBL_EPSILON / 2,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+       } else {
+               testall(0x1.0p100, one, -0x1.0p-100, 0x1.0p100,
+                       ALL_STD_EXCEPT, FE_INEXACT);
+       }
+
+       /* x*y negative, z positive */
+       if (fegetround() == FE_UPWARD || fegetround() == FE_TOWARDZERO) {
+               test(fmaf, -one, one, 0x1.0p-100, -1.0 + FLT_EPSILON / 2,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fma, -one, one, 0x1.0p-200, -1.0 + DBL_EPSILON / 2,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fmal, -one, one, 0x1.0p-200, -1.0 + LDBL_EPSILON / 2,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+       } else {
+               testall(-0x1.0p100, one, 0x1.0p-100, -0x1.0p100,
+                       ALL_STD_EXCEPT, FE_INEXACT);
+       }
+}
+
+/*
+ * Tests for cases where z is very large compared to x*y.
+ */
+static void
+test_big_z(void)
+{
+       /* z positive, x*y positive */
+       if (fegetround() == FE_UPWARD) {
+               test(fmaf, 0x1.0p-50, 0x1.0p-50, 1.0, 1.0 + FLT_EPSILON,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fma, 0x1.0p-100, 0x1.0p-100, 1.0, 1.0 + DBL_EPSILON,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fmal, 0x1.0p-100, 0x1.0p-100, 1.0, 1.0 + LDBL_EPSILON,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+       } else {
+               testall(-0x1.0p-50, -0x1.0p-50, 0x1.0p100, 0x1.0p100,
+                       ALL_STD_EXCEPT, FE_INEXACT);
+       }
+
+       /* z negative, x*y negative */
+       if (fegetround() == FE_DOWNWARD) {
+               test(fmaf, -0x1.0p-50, 0x1.0p-50, -1.0, -(1.0 + FLT_EPSILON),
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fma, -0x1.0p-100, 0x1.0p-100, -1.0, -(1.0 + DBL_EPSILON),
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fmal, -0x1.0p-100, 0x1.0p-100, -1.0, -(1.0 + LDBL_EPSILON),
+                    ALL_STD_EXCEPT, FE_INEXACT);
+       } else {
+               testall(0x1.0p-50, -0x1.0p-50, -0x1.0p100, -0x1.0p100,
+                       ALL_STD_EXCEPT, FE_INEXACT);
+       }
+
+       /* z negative, x*y positive */
+       if (fegetround() == FE_UPWARD || fegetround() == FE_TOWARDZERO) {
+               test(fmaf, -0x1.0p-50, -0x1.0p-50, -1.0,
+                    -1.0 + FLT_EPSILON / 2, ALL_STD_EXCEPT, FE_INEXACT);
+               test(fma, -0x1.0p-100, -0x1.0p-100, -1.0,
+                    -1.0 + DBL_EPSILON / 2, ALL_STD_EXCEPT, FE_INEXACT);
+               test(fmal, -0x1.0p-100, -0x1.0p-100, -1.0,
+                    -1.0 + LDBL_EPSILON / 2, ALL_STD_EXCEPT, FE_INEXACT);
+       } else {
+               testall(0x1.0p-50, 0x1.0p-50, -0x1.0p100, -0x1.0p100,
+                       ALL_STD_EXCEPT, FE_INEXACT);
+       }
+
+       /* z positive, x*y negative */
+       if (fegetround() == FE_DOWNWARD || fegetround() == FE_TOWARDZERO) {
+               test(fmaf, 0x1.0p-50, -0x1.0p-50, 1.0, 1.0 - FLT_EPSILON / 2,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fma, 0x1.0p-100, -0x1.0p-100, 1.0, 1.0 - DBL_EPSILON / 2,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+               test(fmal, 0x1.0p-100, -0x1.0p-100, 1.0, 1.0 - LDBL_EPSILON / 2,
+                    ALL_STD_EXCEPT, FE_INEXACT);
+       } else {
+               testall(-0x1.0p-50, 0x1.0p-50, 0x1.0p100, 0x1.0p100,
+                       ALL_STD_EXCEPT, FE_INEXACT);
+       }
+}
+
+static void
+test_accuracy(void)
+{
+
+       /* ilogb(x*y) - ilogb(z) = 20 */
+       testrnd(fmaf, -0x1.c139d8p-51, -0x1.600e7ap32, 0x1.26558cp-38,
+               0x1.34e48ap-18, 0x1.34e48cp-18, 0x1.34e48ap-18, 0x1.34e48ap-18,
+               ALL_STD_EXCEPT, FE_INEXACT);
+       testrnd(fma, -0x1.c139d7b84f1a3p-51, -0x1.600e7a2a16484p32,
+               0x1.26558cac31580p-38, 0x1.34e48a78aae97p-18,
+               0x1.34e48a78aae97p-18, 0x1.34e48a78aae96p-18,
+               0x1.34e48a78aae96p-18, ALL_STD_EXCEPT, FE_INEXACT);
+#if LDBL_MANT_DIG == 113
+       testrnd(fmal, -0x1.c139d7b84f1a3079263afcc5bae3p-51L,
+               -0x1.600e7a2a164840edbe2e7d301a72p32L,
+               0x1.26558cac315807eb07e448042101p-38L,
+               0x1.34e48a78aae96c76ed36077dd387p-18L,
+               0x1.34e48a78aae96c76ed36077dd388p-18L,
+               0x1.34e48a78aae96c76ed36077dd387p-18L,
+               0x1.34e48a78aae96c76ed36077dd387p-18L,
+               ALL_STD_EXCEPT, FE_INEXACT);
+#elif LDBL_MANT_DIG == 64
+       testrnd(fmal, -0x1.c139d7b84f1a307ap-51L, -0x1.600e7a2a164840eep32L,
+               0x1.26558cac315807ecp-38L, 0x1.34e48a78aae96c78p-18L,
+               0x1.34e48a78aae96c78p-18L, 0x1.34e48a78aae96c76p-18L,
+               0x1.34e48a78aae96c76p-18L, ALL_STD_EXCEPT, FE_INEXACT);
+#elif LDBL_MANT_DIG == 53
+       testrnd(fmal, -0x1.c139d7b84f1a3p-51L, -0x1.600e7a2a16484p32L,
+               0x1.26558cac31580p-38L, 0x1.34e48a78aae97p-18L,
+               0x1.34e48a78aae97p-18L, 0x1.34e48a78aae96p-18L,
+               0x1.34e48a78aae96p-18L, ALL_STD_EXCEPT, FE_INEXACT);
+#endif
+
+       /* ilogb(x*y) - ilogb(z) = -40 */
+       testrnd(fmaf, 0x1.98210ap53, 0x1.9556acp-24, 0x1.d87da4p70,
+               0x1.d87da4p70, 0x1.d87da6p70, 0x1.d87da4p70, 0x1.d87da4p70,
+               ALL_STD_EXCEPT, FE_INEXACT);
+       testrnd(fma, 0x1.98210ac83fe2bp53, 0x1.9556ac1475f0fp-24,
+               0x1.d87da3aafc60ep70, 0x1.d87da3aafda40p70,
+               0x1.d87da3aafda40p70, 0x1.d87da3aafda3fp70,
+               0x1.d87da3aafda3fp70, ALL_STD_EXCEPT, FE_INEXACT);
+#if LDBL_MANT_DIG == 113
+       testrnd(fmal, 0x1.98210ac83fe2a8f65b6278b74cebp53L,
+               0x1.9556ac1475f0f28968b61d0de65ap-24L,
+               0x1.d87da3aafc60d830aa4c6d73b749p70L,
+               0x1.d87da3aafda3f36a69eb86488224p70L,
+               0x1.d87da3aafda3f36a69eb86488225p70L,
+               0x1.d87da3aafda3f36a69eb86488224p70L,
+               0x1.d87da3aafda3f36a69eb86488224p70L,
+               ALL_STD_EXCEPT, FE_INEXACT);
+#elif LDBL_MANT_DIG == 64
+       testrnd(fmal, 0x1.98210ac83fe2a8f6p53L, 0x1.9556ac1475f0f28ap-24L,
+               0x1.d87da3aafc60d83p70L, 0x1.d87da3aafda3f36ap70L,
+               0x1.d87da3aafda3f36ap70L, 0x1.d87da3aafda3f368p70L,
+               0x1.d87da3aafda3f368p70L, ALL_STD_EXCEPT, FE_INEXACT);
+#elif LDBL_MANT_DIG == 53
+       testrnd(fmal, 0x1.98210ac83fe2bp53L, 0x1.9556ac1475f0fp-24L,
+               0x1.d87da3aafc60ep70L, 0x1.d87da3aafda40p70L,
+               0x1.d87da3aafda40p70L, 0x1.d87da3aafda3fp70L,
+               0x1.d87da3aafda3fp70L, ALL_STD_EXCEPT, FE_INEXACT);
+#endif
+
+       /* ilogb(x*y) - ilogb(z) = 0 */
+       testrnd(fmaf, 0x1.31ad02p+100, 0x1.2fbf7ap-42, -0x1.c3e106p+58,
+               -0x1.64c27cp+56, -0x1.64c27ap+56, -0x1.64c27cp+56,
+               -0x1.64c27ap+56, ALL_STD_EXCEPT, FE_INEXACT);
+       testrnd(fma, 0x1.31ad012ede8aap+100, 0x1.2fbf79c839067p-42,
+               -0x1.c3e106929056ep+58, -0x1.64c282b970a5fp+56,
+               -0x1.64c282b970a5ep+56, -0x1.64c282b970a5fp+56,
+               -0x1.64c282b970a5ep+56, ALL_STD_EXCEPT, FE_INEXACT);
+#if LDBL_MANT_DIG == 113
+       testrnd(fmal, 0x1.31ad012ede8aa282fa1c19376d16p+100L,
+                0x1.2fbf79c839066f0f5c68f6d2e814p-42L,
+               -0x1.c3e106929056ec19de72bfe64215p+58L,
+               -0x1.64c282b970a612598fc025ca8cddp+56L,
+               -0x1.64c282b970a612598fc025ca8cddp+56L,
+               -0x1.64c282b970a612598fc025ca8cdep+56L,
+               -0x1.64c282b970a612598fc025ca8cddp+56L,
+               ALL_STD_EXCEPT, FE_INEXACT);
+#elif LDBL_MANT_DIG == 64
+       testrnd(fmal, 0x1.31ad012ede8aa4eap+100L, 0x1.2fbf79c839066aeap-42L,
+               -0x1.c3e106929056e61p+58L, -0x1.64c282b970a60298p+56L,
+               -0x1.64c282b970a60298p+56L, -0x1.64c282b970a6029ap+56L,
+               -0x1.64c282b970a60298p+56L, ALL_STD_EXCEPT, FE_INEXACT);
+#elif LDBL_MANT_DIG == 53
+       testrnd(fmal, 0x1.31ad012ede8aap+100L, 0x1.2fbf79c839067p-42L,
+               -0x1.c3e106929056ep+58L, -0x1.64c282b970a5fp+56L,
+               -0x1.64c282b970a5ep+56L, -0x1.64c282b970a5fp+56L,
+               -0x1.64c282b970a5ep+56L, ALL_STD_EXCEPT, FE_INEXACT);
+#endif
+
+       /* x*y (rounded) ~= -z */
+       /* XXX spurious inexact exceptions */
+       testrnd(fmaf, 0x1.bbffeep-30, -0x1.1d164cp-74, 0x1.ee7296p-104,
+               -0x1.c46ea8p-128, -0x1.c46ea8p-128, -0x1.c46ea8p-128,
+               -0x1.c46ea8p-128, ALL_STD_EXCEPT & ~FE_INEXACT, 0);
+       testrnd(fma, 0x1.bbffeea6fc7d6p-30, 0x1.1d164c6cbf078p-74,
+               -0x1.ee72993aff948p-104, -0x1.71f72ac7d9d8p-159,
+               -0x1.71f72ac7d9d8p-159, -0x1.71f72ac7d9d8p-159,
+               -0x1.71f72ac7d9d8p-159, ALL_STD_EXCEPT & ~FE_INEXACT, 0);
+#if LDBL_MANT_DIG == 113
+       testrnd(fmal, 0x1.bbffeea6fc7d65927d147f437675p-30L,
+               0x1.1d164c6cbf078b7a22607d1cd6a2p-74L,
+               -0x1.ee72993aff94973876031bec0944p-104L,
+               0x1.64e086175b3a2adc36e607058814p-217L,
+               0x1.64e086175b3a2adc36e607058814p-217L,
+               0x1.64e086175b3a2adc36e607058814p-217L,
+               0x1.64e086175b3a2adc36e607058814p-217L,
+               ALL_STD_EXCEPT & ~FE_INEXACT, 0);
+#elif LDBL_MANT_DIG == 64
+       testrnd(fmal, 0x1.bbffeea6fc7d6592p-30L, 0x1.1d164c6cbf078b7ap-74L,
+               -0x1.ee72993aff949736p-104L, 0x1.af190e7a1ee6ad94p-168L,
+               0x1.af190e7a1ee6ad94p-168L, 0x1.af190e7a1ee6ad94p-168L,
+               0x1.af190e7a1ee6ad94p-168L, ALL_STD_EXCEPT & ~FE_INEXACT, 0);
+#elif LDBL_MANT_DIG == 53
+       testrnd(fmal, 0x1.bbffeea6fc7d6p-30L, 0x1.1d164c6cbf078p-74L,
+               -0x1.ee72993aff948p-104L, -0x1.71f72ac7d9d8p-159L,
+               -0x1.71f72ac7d9d8p-159L, -0x1.71f72ac7d9d8p-159L,
+               -0x1.71f72ac7d9d8p-159L, ALL_STD_EXCEPT & ~FE_INEXACT, 0);
+#endif
+}
+
+static void
+test_double_rounding(void)
+{
+
+       /*
+        *     a =  0x1.8000000000001p0
+        *     b =  0x1.8000000000001p0
+        *     c = -0x0.0000000000000000000000000080...1p+1
+        * a * b =  0x1.2000000000001800000000000080p+1
+        *
+        * The correct behavior is to round DOWN to 0x1.2000000000001p+1 in
+        * round-to-nearest mode.  An implementation that computes a*b+c in
+        * double+double precision, however, will get 0x1.20000000000018p+1,
+        * and then round UP.
+        */
+       fesetround(FE_TONEAREST);
+       test(fma, 0x1.8000000000001p0, 0x1.8000000000001p0,
+            -0x1.0000000000001p-104, 0x1.2000000000001p+1,
+            ALL_STD_EXCEPT, FE_INEXACT);
+       fesetround(FE_DOWNWARD);
+       test(fma, 0x1.8000000000001p0, 0x1.8000000000001p0,
+            -0x1.0000000000001p-104, 0x1.2000000000001p+1,
+            ALL_STD_EXCEPT, FE_INEXACT);
+       fesetround(FE_UPWARD);
+       test(fma, 0x1.8000000000001p0, 0x1.8000000000001p0,
+            -0x1.0000000000001p-104, 0x1.2000000000002p+1,
+            ALL_STD_EXCEPT, FE_INEXACT);
+
+       fesetround(FE_TONEAREST);
+       test(fmaf, 0x1.800002p+0, 0x1.800002p+0, -0x1.000002p-46, 0x1.200002p+1,
+            ALL_STD_EXCEPT, FE_INEXACT);
+       fesetround(FE_DOWNWARD);
+       test(fmaf, 0x1.800002p+0, 0x1.800002p+0, -0x1.000002p-46, 0x1.200002p+1,
+            ALL_STD_EXCEPT, FE_INEXACT);
+       fesetround(FE_UPWARD);
+       test(fmaf, 0x1.800002p+0, 0x1.800002p+0, -0x1.000002p-46, 0x1.200004p+1,
+            ALL_STD_EXCEPT, FE_INEXACT);
+
+       fesetround(FE_TONEAREST);
+#if LDBL_MANT_DIG == 64
+       test(fmal, 0x1.4p+0L, 0x1.0000000000000004p+0L, 0x1p-128L,
+            0x1.4000000000000006p+0L, ALL_STD_EXCEPT, FE_INEXACT);
+#elif LDBL_MANT_DIG == 113
+       test(fmal, 0x1.8000000000000000000000000001p+0L,
+            0x1.8000000000000000000000000001p+0L,
+            -0x1.0000000000000000000000000001p-224L,
+            0x1.2000000000000000000000000001p+1L, ALL_STD_EXCEPT, FE_INEXACT);
+#endif
+
+}
+
+static const int rmodes[] = {
+       FE_TONEAREST, FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO
+};
+
+ATF_TC_WITHOUT_HEAD(zeroes);
+ATF_TC_BODY(zeroes, tc)
+{
+       size_t i;
+       for (i = 0; i < nitems(rmodes); i++) {
+               printf("rmode = %d\n", rmodes[i]);
+               fesetround(rmodes[i]);
+               test_zeroes();
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(infinities);
+ATF_TC_BODY(infinities, tc)
+{
+       size_t i;
+       for (i = 0; i < nitems(rmodes); i++) {
+               printf("rmode = %d\n", rmodes[i]);
+               fesetround(rmodes[i]);
+               test_infinities();
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(nans);
+ATF_TC_BODY(nans, tc)
+{
+       fesetround(FE_TONEAREST);
+       test_nans();
+}
+
+
+ATF_TC_WITHOUT_HEAD(small_z);
+ATF_TC_BODY(small_z, tc)
+{
+       size_t i;
+       for (i = 0; i < nitems(rmodes); i++) {
+               printf("rmode = %d\n", rmodes[i]);
+               fesetround(rmodes[i]);
+               test_small_z();
+       }
+}
+
+
+ATF_TC_WITHOUT_HEAD(big_z);
+ATF_TC_BODY(big_z, tc)
+{
+       size_t i;
+       for (i = 0; i < nitems(rmodes); i++) {
+               printf("rmode = %d\n", rmodes[i]);
+               fesetround(rmodes[i]);
+               test_big_z();
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(accuracy);
+ATF_TC_BODY(accuracy, tc)
+{
+       fesetround(FE_TONEAREST);
+       test_accuracy();
+}
+
+ATF_TC_WITHOUT_HEAD(double_rounding);
+ATF_TC_BODY(double_rounding, tc) {
+       test_double_rounding();
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, zeroes);
+       ATF_TP_ADD_TC(tp, infinities);
+       ATF_TP_ADD_TC(tp, nans);
+       ATF_TP_ADD_TC(tp, small_z);
+       ATF_TP_ADD_TC(tp, big_z);
+       ATF_TP_ADD_TC(tp, accuracy);
+       ATF_TP_ADD_TC(tp, double_rounding);
+       /*
+        * TODO:
+        * - Tests for subnormals
+        * - Cancellation tests (e.g., z = (double)x*y, but x*y is inexact)
+        */
+       return (atf_no_error());
+}
diff --git a/regress/lib/libm/msun/fmaxmin_test.c b/regress/lib/libm/msun/fmaxmin_test.c
new file mode 100644 (file)
index 0000000..e84a94c
--- /dev/null
@@ -0,0 +1,188 @@
+/*     $OpenBSD: fmaxmin_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $  */
+/*-
+ * Copyright (c) 2008 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Tests for fmax{,f,l}() and fmin{,f,l}.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "test-utils.h"
+
+#pragma STDC FENV_ACCESS ON
+
+/*
+ * Test whether func(x, y) has the expected result, and make sure no
+ * exceptions are raised.
+ */
+#define        TEST(func, type, x, y, expected, rmode) do {                          \
+       type __x = (x); /* convert before we clear exceptions */              \
+       type __y = (y);                                                       \
+       ATF_REQUIRE_EQ(0, feclearexcept(ALL_STD_EXCEPT));                     \
+       long double __result = func((__x), (__y));                            \
+       CHECK_FP_EXCEPTIONS_MSG(0, ALL_STD_EXCEPT,                            \
+           #func "(%.20Lg, %.20Lg) rmode%d", (x), (y), rmode);               \
+       ATF_CHECK_MSG(fpequal_cs(__result, (expected), true),                 \
+           #func "(%.20Lg, %.20Lg) rmode%d = %.20Lg, expected %.20Lg\n",     \
+           (x), (y), rmode, __result, (expected));                           \
+} while (0)
+
+static void
+testall_r(long double big, long double small, int rmode)
+{
+       long double expected_max = isnan(big) ? small : big;
+       long double expected_min = isnan(small) ? big : small;
+       TEST(fmaxf, float, big, small, expected_max, rmode);
+       TEST(fmaxf, float, small, big, expected_max, rmode);
+       TEST(fmax, double, big, small, expected_max, rmode);
+       TEST(fmax, double, small, big, expected_max, rmode);
+       TEST(fmaxl, long double, big, small, expected_max, rmode);
+       TEST(fmaxl, long double, small, big, expected_max, rmode);
+       TEST(fminf, float, big, small, expected_min, rmode);
+       TEST(fminf, float, small, big, expected_min, rmode);
+       TEST(fmin, double, big, small, expected_min, rmode);
+       TEST(fmin, double, small, big, expected_min, rmode);
+       TEST(fminl, long double, big, small, expected_min, rmode);
+       TEST(fminl, long double, small, big, expected_min, rmode);
+}
+
+/*
+ * Test all the functions: fmaxf, fmax, fmaxl, fminf, fmin, and fminl,
+ * in all rounding modes and with the arguments in different orders.
+ * The input 'big' must be >= 'small'.
+ */
+static void
+testall(long double big, long double small)
+{
+       static const int rmodes[] = {
+               FE_TONEAREST, FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO
+       };
+       int i;
+
+       for (i = 0; i < 4; i++) {
+               fesetround(rmodes[i]);
+               testall_r(big, small, rmodes[i]);
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(test1);
+ATF_TC_BODY(test1, tc)
+{
+       testall(1.0, 0.0);
+}
+
+ATF_TC_WITHOUT_HEAD(test2);
+ATF_TC_BODY(test2, tc)
+{
+       testall(42.0, nextafterf(42.0, -INFINITY));
+}
+ATF_TC_WITHOUT_HEAD(test3);
+ATF_TC_BODY(test3, tc)
+{
+       testall(nextafterf(42.0, INFINITY), 42.0);
+}
+
+ATF_TC_WITHOUT_HEAD(test4);
+ATF_TC_BODY(test4, tc)
+{
+       testall(-5.0, -5.0);
+}
+
+ATF_TC_WITHOUT_HEAD(test5);
+ATF_TC_BODY(test5, tc)
+{
+       testall(-3.0, -4.0);
+}
+
+ATF_TC_WITHOUT_HEAD(test6);
+ATF_TC_BODY(test6, tc)
+{
+       testall(1.0, NAN);
+}
+ATF_TC_WITHOUT_HEAD(test7);
+ATF_TC_BODY(test7, tc)
+{
+       testall(INFINITY, NAN);
+}
+
+ATF_TC_WITHOUT_HEAD(test8);
+ATF_TC_BODY(test8, tc)
+{
+       testall(INFINITY, 1.0);
+}
+
+ATF_TC_WITHOUT_HEAD(test9);
+ATF_TC_BODY(test9, tc)
+{
+       testall(-3.0, -INFINITY);
+}
+
+ATF_TC_WITHOUT_HEAD(test10);
+ATF_TC_BODY(test10, tc)
+{
+       testall(3.0, -INFINITY);
+}
+
+ATF_TC_WITHOUT_HEAD(test11);
+ATF_TC_BODY(test11, tc)
+{
+       testall(NAN, NAN);
+}
+
+ATF_TC_WITHOUT_HEAD(test12);
+ATF_TC_BODY(test12, tc)
+{
+       /* This test isn't strictly required to work by C99. */
+       testall(0.0, -0.0);
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, test1);
+       ATF_TP_ADD_TC(tp, test2);
+       ATF_TP_ADD_TC(tp, test3);
+       ATF_TP_ADD_TC(tp, test4);
+       ATF_TP_ADD_TC(tp, test5);
+       ATF_TP_ADD_TC(tp, test6);
+       ATF_TP_ADD_TC(tp, test7);
+       ATF_TP_ADD_TC(tp, test8);
+       ATF_TP_ADD_TC(tp, test9);
+       ATF_TP_ADD_TC(tp, test10);
+       ATF_TP_ADD_TC(tp, test11);
+       ATF_TP_ADD_TC(tp, test12);
+
+       return (atf_no_error());
+}
index bd516a8..3d482ef 100644 (file)
@@ -1,3 +1,4 @@
+/*     $OpenBSD: ilogb_test.c,v 1.2 2021/10/22 18:00:22 mbuhl Exp $    */
 /*-
  * Copyright (c) 2004 Stefan Farfeleder
  * All rights reserved.
  * $FreeBSD: head/lib/msun/tests/ilogb_test.c 292328 2015-12-16 09:11:11Z ngie $
  */
 
-#include <assert.h>
 #include <float.h>
 #include <limits.h>
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 
-int
-main(void)
+#include "test-utils.h"
+
+ATF_TC_WITHOUT_HEAD(ilogb);
+ATF_TC_BODY(ilogb, tc)
 {
        char buf[128], *end;
        double d;
-       float f;
-       long double ld;
        int e, i;
 
-       printf("1..3\n");
-       assert(ilogb(0) == FP_ILOGB0);
-       assert(ilogb(NAN) == FP_ILOGBNAN);
-       assert(ilogb(INFINITY) == INT_MAX);
+       ATF_CHECK_EQ(FP_ILOGB0, ilogb(0));
+       ATF_CHECK_EQ(FP_ILOGBNAN, ilogb(NAN));
+       ATF_CHECK_EQ(INT_MAX, ilogb(INFINITY));
        for (e = DBL_MIN_EXP - DBL_MANT_DIG; e < DBL_MAX_EXP; e++) {
                snprintf(buf, sizeof(buf), "0x1.p%d", e);
                d = strtod(buf, &end);
-               assert(*end == '\0');
+               ATF_CHECK_EQ('\0', *end);
                i = ilogb(d);
-               assert(i == e);
+               ATF_CHECK_EQ_MSG(e, i, "ilogb(%g) returned %d not %d", d, i, e);
        }
-       printf("ok 1 - ilogb\n");
+}
+
+ATF_TC_WITHOUT_HEAD(ilogbf);
+ATF_TC_BODY(ilogbf, tc)
+{
+       char buf[128], *end;
+       float f;
+       int e, i;
 
-       assert(ilogbf(0) == FP_ILOGB0);
-       assert(ilogbf(NAN) == FP_ILOGBNAN);
-       assert(ilogbf(INFINITY) == INT_MAX);
+       ATF_CHECK_EQ(FP_ILOGB0, ilogbf(0));
+       ATF_CHECK_EQ(FP_ILOGBNAN, ilogbf(NAN));
+       ATF_CHECK_EQ(INT_MAX, ilogbf(INFINITY));
        for (e = FLT_MIN_EXP - FLT_MANT_DIG; e < FLT_MAX_EXP; e++) {
                snprintf(buf, sizeof(buf), "0x1.p%d", e);
                f = strtof(buf, &end);
-               assert(*end == '\0');
+               ATF_CHECK_EQ('\0', *end);
                i = ilogbf(f);
-               assert(i == e);
+               ATF_CHECK_EQ_MSG(e, i, "ilogbf(%g) returned %d not %d", f, i,
+                   e);
        }
-       printf("ok 2 - ilogbf\n");
+}
 
-       assert(ilogbl(0) == FP_ILOGB0);
-       assert(ilogbl(NAN) == FP_ILOGBNAN);
-       assert(ilogbl(INFINITY) == INT_MAX);
+ATF_TC_WITHOUT_HEAD(ilogbl);
+ATF_TC_BODY(ilogbl, tc)
+{
+       char buf[128], *end;
+       long double ld;
+       int e, i;
+
+       ATF_CHECK_EQ(FP_ILOGB0, ilogbl(0));
+       ATF_CHECK_EQ(FP_ILOGBNAN, ilogbl(NAN));
+       ATF_CHECK_EQ(INT_MAX, ilogbl(INFINITY));
        for (e = LDBL_MIN_EXP - LDBL_MANT_DIG; e < LDBL_MAX_EXP; e++) {
                snprintf(buf, sizeof(buf), "0x1.p%d", e);
                ld = strtold(buf, &end);
-               assert(*end == '\0');
+               ATF_CHECK_EQ('\0', *end);
                i = ilogbl(ld);
-               assert(i == e);
+               ATF_CHECK_EQ_MSG(e, i, "ilogbl(%Lg) returned %d not %d", ld, i,
+                   e);
        }
-       printf("ok 3 - ilogbl\n");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, ilogb);
+       ATF_TP_ADD_TC(tp, ilogbf);
+       ATF_TP_ADD_TC(tp, ilogbl);
 
-       return (0);
+       return (atf_no_error());
 }
diff --git a/regress/lib/libm/msun/invctrig_test.c b/regress/lib/libm/msun/invctrig_test.c
new file mode 100644 (file)
index 0000000..9502ddf
--- /dev/null
@@ -0,0 +1,354 @@
+/*     $OpenBSD: invctrig_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $ */
+/*-
+ * Copyright (c) 2008-2013 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Tests for casin[h](), cacos[h](), and catan[h]().
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <complex.h>
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "test-utils.h"
+
+#pragma        STDC FENV_ACCESS        ON
+#pragma        STDC CX_LIMITED_RANGE   OFF
+
+/*
+ * Test that a function returns the correct value and sets the
+ * exception flags correctly. The exceptmask specifies which
+ * exceptions we should check. We need to be lenient for several
+ * reasons, but mainly because on some architectures it's impossible
+ * to raise FE_OVERFLOW without raising FE_INEXACT.
+ *
+ * These are macros instead of functions so that assert provides more
+ * meaningful error messages.
+ *
+ * XXX The volatile here is to avoid gcc's bogus constant folding and work
+ *     around the lack of support for the FENV_ACCESS pragma.
+ */
+#define        test_p(func, z, result, exceptmask, excepts, checksign) do {    \
+       volatile long double complex _d = z;                            \
+       debug("  testing %s(%Lg + %Lg I) == %Lg + %Lg I\n", #func,      \
+           creall(_d), cimagl(_d), creall(result), cimagl(result));    \
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));                \
+       CHECK_CFPEQUAL_CS((func)(_d), (result), (checksign));           \
+       CHECK_FP_EXCEPTIONS_MSG(excepts, exceptmask, "for %s(%s)",      \
+           #func, #z);                                                 \
+} while (0)
+
+/*
+ * Test within a given tolerance.  The tolerance indicates relative error
+ * in ulps.
+ */
+#define        test_p_tol(func, z, result, tol)                        do {    \
+       debug("  testing %s(%Lg + %Lg I) ~= %Lg + %Lg I\n", #func,      \
+           creall(z), cimagl(z), creall(result), cimagl(result));      \
+       CHECK_CFPEQUAL_TOL((func)(z), (result), (tol), CS_BOTH);        \
+} while (0)
+
+/* These wrappers apply the identities f(conj(z)) = conj(f(z)). */
+#define        test(func, z, result, exceptmask, excepts, checksign)   do {    \
+       test_p(func, z, result, exceptmask, excepts, checksign);        \
+       test_p(func, conjl(z), conjl(result), exceptmask, excepts, checksign); \
+} while (0)
+#define        test_tol(func, z, result, tol)                          do {    \
+       test_p_tol(func, z, result, tol);                               \
+       test_p_tol(func, conjl(z), conjl(result), tol);                 \
+} while (0)
+
+/* Test the given function in all precisions. */
+#define        testall(func, x, result, exceptmask, excepts, checksign) do {   \
+       test(func, x, result, exceptmask, excepts, checksign);          \
+       test(func##f, x, result, exceptmask, excepts, checksign);       \
+} while (0)
+#define        testall_odd(func, x, result, exceptmask, excepts, checksign) do { \
+       testall(func, x, result, exceptmask, excepts, checksign);       \
+       testall(func, -(x), -result, exceptmask, excepts, checksign);   \
+} while (0)
+#define        testall_even(func, x, result, exceptmask, excepts, checksign) do { \
+       testall(func, x, result, exceptmask, excepts, checksign);       \
+       testall(func, -(x), result, exceptmask, excepts, checksign);    \
+} while (0)
+
+/*
+ * Test the given function in all precisions, within a given tolerance.
+ * The tolerance is specified in ulps.
+ */
+#define        testall_tol(func, x, result, tol)                          do { \
+       test_tol(func, x, result, (tol) * DBL_ULP());                   \
+       test_tol(func##f, x, result, (tol) * FLT_ULP());                \
+} while (0)
+#define        testall_odd_tol(func, x, result, tol)                      do { \
+       testall_tol(func, x, result, tol);                              \
+       testall_tol(func, -(x), -result, tol);                          \
+} while (0)
+#define        testall_even_tol(func, x, result, tol)                     do { \
+       testall_tol(func, x, result, tol);                              \
+       testall_tol(func, -(x), result, tol);                           \
+} while (0)
+
+static const long double
+pi = 3.14159265358979323846264338327950280L,
+c3pi = 9.42477796076937971538793014983850839L;
+
+
+/* Tests for 0 */
+ATF_TC_WITHOUT_HEAD(zero);
+ATF_TC_BODY(zero, tc)
+{
+       long double complex zero = CMPLXL(0.0, 0.0);
+
+       testall_tol(cacosh, zero, CMPLXL(0.0, pi / 2), 1);
+       testall_tol(cacosh, -zero, CMPLXL(0.0, -pi / 2), 1);
+       testall_tol(cacos, zero, CMPLXL(pi / 2, -0.0), 1);
+       testall_tol(cacos, -zero, CMPLXL(pi / 2, 0.0), 1);
+
+       testall_odd(casinh, zero, zero, ALL_STD_EXCEPT, 0, CS_BOTH);
+       testall_odd(casin, zero, zero, ALL_STD_EXCEPT, 0, CS_BOTH);
+
+       testall_odd(catanh, zero, zero, ALL_STD_EXCEPT, 0, CS_BOTH);
+       testall_odd(catan, zero, zero, ALL_STD_EXCEPT, 0, CS_BOTH);
+}
+
+/*
+ * Tests for NaN inputs.
+ */
+ATF_TC_WITHOUT_HEAD(nan);
+ATF_TC_BODY(nan, tc)
+{
+       long double complex nan_nan = CMPLXL(NAN, NAN);
+       long double complex z;
+
+       /*
+        * IN           CACOSH      CACOS       CASINH      CATANH
+        * NaN,NaN      NaN,NaN     NaN,NaN     NaN,NaN     NaN,NaN
+        * finite,NaN   NaN,NaN*    NaN,NaN*    NaN,NaN*    NaN,NaN*
+        * NaN,finite   NaN,NaN*    NaN,NaN*    NaN,NaN*    NaN,NaN*
+        * NaN,Inf      Inf,NaN     NaN,-Inf    ?Inf,NaN    ?0,pi/2
+        * +-Inf,NaN    Inf,NaN     NaN,?Inf    +-Inf,NaN   +-0,NaN
+        * +-0,NaN      NaN,NaN*    pi/2,NaN    NaN,NaN*    +-0,NaN
+        * NaN,0        NaN,NaN*    NaN,NaN*    NaN,0       NaN,NaN*
+        *
+        *  * = raise invalid
+        */
+       z = nan_nan;
+       testall(cacosh, z, nan_nan, ALL_STD_EXCEPT, 0, 0);
+       testall(cacos, z, nan_nan, ALL_STD_EXCEPT, 0, 0);
+       testall(casinh, z, nan_nan, ALL_STD_EXCEPT, 0, 0);
+       testall(casin, z, nan_nan, ALL_STD_EXCEPT, 0, 0);
+       testall(catanh, z, nan_nan, ALL_STD_EXCEPT, 0, 0);
+       testall(catan, z, nan_nan, ALL_STD_EXCEPT, 0, 0);
+
+       z = CMPLXL(0.5, NAN);
+       testall(cacosh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(cacos, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(casinh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(casin, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(catanh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(catan, z, nan_nan, OPT_INVALID, 0, 0);
+
+       z = CMPLXL(NAN, 0.5);
+       testall(cacosh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(cacos, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(casinh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(casin, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(catanh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(catan, z, nan_nan, OPT_INVALID, 0, 0);
+
+       z = CMPLXL(NAN, INFINITY);
+       testall(cacosh, z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0, CS_REAL);
+       testall(cacosh, -z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0, CS_REAL);
+       testall(cacos, z, CMPLXL(NAN, -INFINITY), ALL_STD_EXCEPT, 0, CS_IMAG);
+       testall(casinh, z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0, 0);
+       testall(casin, z, CMPLXL(NAN, INFINITY), ALL_STD_EXCEPT, 0, CS_IMAG);
+       testall_tol(catanh, z, CMPLXL(0.0, pi / 2), 1);
+       testall(catan, z, CMPLXL(NAN, 0.0), ALL_STD_EXCEPT, 0, CS_IMAG);
+
+       z = CMPLXL(INFINITY, NAN);
+       testall_even(cacosh, z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0,
+                    CS_REAL);
+       testall_even(cacos, z, CMPLXL(NAN, INFINITY), ALL_STD_EXCEPT, 0, 0);
+       testall_odd(casinh, z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0,
+                   CS_REAL);
+       testall_odd(casin, z, CMPLXL(NAN, INFINITY), ALL_STD_EXCEPT, 0, 0);
+       testall_odd(catanh, z, CMPLXL(0.0, NAN), ALL_STD_EXCEPT, 0, CS_REAL);
+       testall_odd_tol(catan, z, CMPLXL(pi / 2, 0.0), 1);
+
+       z = CMPLXL(0.0, NAN);
+        /* XXX We allow a spurious inexact exception here. */
+       testall_even(cacosh, z, nan_nan, OPT_INVALID & ~FE_INEXACT, 0, 0);
+       testall_even_tol(cacos, z, CMPLXL(pi / 2, NAN), 1);
+       testall_odd(casinh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall_odd(casin, z, CMPLXL(0.0, NAN), ALL_STD_EXCEPT, 0, CS_REAL);
+       testall_odd(catanh, z, CMPLXL(0.0, NAN), OPT_INVALID, 0, CS_REAL);
+       testall_odd(catan, z, nan_nan, OPT_INVALID, 0, 0);
+
+       z = CMPLXL(NAN, 0.0);
+       testall(cacosh, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(cacos, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(casinh, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, 0, CS_IMAG);
+       testall(casin, z, nan_nan, OPT_INVALID, 0, 0);
+       testall(catanh, z, nan_nan, OPT_INVALID, 0, CS_IMAG);
+       testall(catan, z, CMPLXL(NAN, 0.0), ALL_STD_EXCEPT, 0, 0);
+}
+
+ATF_TC_WITHOUT_HEAD(inf);
+ATF_TC_BODY(inf, tc)
+{
+       long double complex z;
+
+       /*
+        * IN           CACOSH      CACOS       CASINH      CATANH
+        * Inf,Inf      Inf,pi/4    pi/4,-Inf   Inf,pi/4    0,pi/2
+        * -Inf,Inf     Inf,3pi/4   3pi/4,-Inf  ---         ---
+        * Inf,finite   Inf,0       0,-Inf      Inf,0       0,pi/2
+        * -Inf,finite  Inf,pi      pi,-Inf     ---         ---
+        * finite,Inf   Inf,pi/2    pi/2,-Inf   Inf,pi/2    0,pi/2
+        */
+       z = CMPLXL(INFINITY, INFINITY);
+       testall_tol(cacosh, z, CMPLXL(INFINITY, pi / 4), 1);
+       testall_tol(cacosh, -z, CMPLXL(INFINITY, -c3pi / 4), 1);
+       testall_tol(cacos, z, CMPLXL(pi / 4, -INFINITY), 1);
+       testall_tol(cacos, -z, CMPLXL(c3pi / 4, INFINITY), 1);
+       testall_odd_tol(casinh, z, CMPLXL(INFINITY, pi / 4), 1);
+       testall_odd_tol(casin, z, CMPLXL(pi / 4, INFINITY), 1);
+       testall_odd_tol(catanh, z, CMPLXL(0, pi / 2), 1);
+       testall_odd_tol(catan, z, CMPLXL(pi / 2, 0), 1);
+
+       z = CMPLXL(INFINITY, 0.5);
+       /* XXX We allow a spurious inexact exception here. */
+       testall(cacosh, z, CMPLXL(INFINITY, 0), OPT_INEXACT, 0, CS_BOTH);
+       testall_tol(cacosh, -z, CMPLXL(INFINITY, -pi), 1);
+       testall(cacos, z, CMPLXL(0, -INFINITY), OPT_INEXACT, 0, CS_BOTH);
+       testall_tol(cacos, -z, CMPLXL(pi, INFINITY), 1);
+       testall_odd(casinh, z, CMPLXL(INFINITY, 0), OPT_INEXACT, 0, CS_BOTH);
+       testall_odd_tol(casin, z, CMPLXL(pi / 2, INFINITY), 1);
+       testall_odd_tol(catanh, z, CMPLXL(0, pi / 2), 1);
+       testall_odd_tol(catan, z, CMPLXL(pi / 2, 0), 1);
+
+       z = CMPLXL(0.5, INFINITY);
+       testall_tol(cacosh, z, CMPLXL(INFINITY, pi / 2), 1);
+       testall_tol(cacosh, -z, CMPLXL(INFINITY, -pi / 2), 1);
+       testall_tol(cacos, z, CMPLXL(pi / 2, -INFINITY), 1);
+       testall_tol(cacos, -z, CMPLXL(pi / 2, INFINITY), 1);
+       testall_odd_tol(casinh, z, CMPLXL(INFINITY, pi / 2), 1);
+       /* XXX We allow a spurious inexact exception here. */
+       testall_odd(casin, z, CMPLXL(0.0, INFINITY), OPT_INEXACT, 0, CS_BOTH);
+       testall_odd_tol(catanh, z, CMPLXL(0, pi / 2), 1);
+       testall_odd_tol(catan, z, CMPLXL(pi / 2, 0), 1);
+}
+
+/* Tests along the real and imaginary axes. */
+ATF_TC_WITHOUT_HEAD(axes);
+ATF_TC_BODY(axes, tc)
+{
+       static const long double nums[] = {
+               -2, -1, -0.5, 0.5, 1, 2
+       };
+       long double complex z;
+       unsigned i;
+
+       for (i = 0; i < nitems(nums); i++) {
+               /* Real axis */
+               z = CMPLXL(nums[i], 0.0);
+               if (fabsl(nums[i]) <= 1) {
+                       testall_tol(cacosh, z, CMPLXL(0.0, acos(nums[i])), 1);
+                       testall_tol(cacos, z, CMPLXL(acosl(nums[i]), -0.0), 1);
+                       testall_tol(casin, z, CMPLXL(asinl(nums[i]), 0.0), 1);
+                       testall_tol(catanh, z, CMPLXL(atanh(nums[i]), 0.0), 1);
+               } else {
+                       testall_tol(cacosh, z,
+                                   CMPLXL(acosh(fabsl(nums[i])),
+                                          (nums[i] < 0) ? pi : 0), 1);
+                       testall_tol(cacos, z,
+                                   CMPLXL((nums[i] < 0) ? pi : 0,
+                                          -acosh(fabsl(nums[i]))), 1);
+                       testall_tol(casin, z,
+                                   CMPLXL(copysign(pi / 2, nums[i]),
+                                          acosh(fabsl(nums[i]))), 1);
+                       testall_tol(catanh, z,
+                                   CMPLXL(atanh(1 / nums[i]), pi / 2), 1);
+               }
+               testall_tol(casinh, z, CMPLXL(asinh(nums[i]), 0.0), 1);
+               testall_tol(catan, z, CMPLXL(atan(nums[i]), 0), 1);
+
+               /* TODO: Test the imaginary axis. */
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(small);
+ATF_TC_BODY(small, tc)
+{
+       /*
+        * z =  0.75 + i 0.25
+        *     acos(z) = Pi/4 - i ln(2)/2
+        *     asin(z) = Pi/4 + i ln(2)/2
+        *     atan(z) = atan(4)/2 + i ln(17/9)/4
+        */
+       complex long double z;
+       complex long double acos_z;
+       complex long double asin_z;
+       complex long double atan_z;
+
+       z = CMPLXL(0.75L, 0.25L);
+       acos_z = CMPLXL(pi / 4, -0.34657359027997265470861606072908828L);
+       asin_z = CMPLXL(pi / 4, 0.34657359027997265470861606072908828L);
+       atan_z = CMPLXL(0.66290883183401623252961960521423782L,
+                        0.15899719167999917436476103600701878L);
+
+       testall_tol(cacos, z, acos_z, 2);
+       testall_odd_tol(casin, z, asin_z, 2);
+       testall_odd_tol(catan, z, atan_z, 2);
+}
+
+/* Test inputs that might cause overflow in a sloppy implementation. */
+ATF_TC_WITHOUT_HEAD(large);
+ATF_TC_BODY(large, tc)
+{
+       /* TODO: Write these tests */
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, zero);
+       ATF_TP_ADD_TC(tp, nan);
+       ATF_TP_ADD_TC(tp, inf);
+       ATF_TP_ADD_TC(tp, axes);
+       ATF_TP_ADD_TC(tp, small);
+       ATF_TP_ADD_TC(tp, large);
+
+       return (atf_no_error());
+}
diff --git a/regress/lib/libm/msun/invtrig_test.c b/regress/lib/libm/msun/invtrig_test.c
new file mode 100644 (file)
index 0000000..7c06a89
--- /dev/null
@@ -0,0 +1,460 @@
+/*     $OpenBSD: invtrig_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $  */
+/*-
+ * Copyright (c) 2008 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Tests for corner cases in the inverse trigonometric functions. Some
+ * accuracy tests are included as well, but these are very basic
+ * sanity checks, not intended to be comprehensive.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "test-utils.h"
+
+#pragma STDC FENV_ACCESS ON
+
+/*
+ * Test that a function returns the correct value and sets the
+ * exception flags correctly. A tolerance specifying the maximum
+ * relative error allowed may be specified. For the 'testall'
+ * functions, the tolerance is specified in ulps.
+ *
+ * These are macros instead of functions so that assert provides more
+ * meaningful error messages.
+ */
+#define        test_tol(func, x, result, tol, excepts) do {                    \
+       volatile long double _in = (x), _out = (result);                \
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));                \
+       CHECK_FPEQUAL_TOL(func(_in), _out, (tol), CS_BOTH);             \
+       CHECK_FP_EXCEPTIONS_MSG(excepts, ALL_STD_EXCEPT, "for %s(%s)",  \
+           #func, #x);                                                 \
+} while (0)
+#define test(func, x, result, excepts)                                 \
+       test_tol(func, (x), (result), 0, (excepts))
+
+#define        _testall_tol(prefix, x, result, tol, excepts) do {              \
+       test_tol(prefix, (double)(x), (double)(result),                 \
+                (tol) * ldexp(1.0, 1 - DBL_MANT_DIG), (excepts));      \
+       test_tol(prefix##f, (float)(x), (float)(result),                \
+                (tol) * ldexpf(1.0, 1 - FLT_MANT_DIG), (excepts));     \
+} while (0)
+
+#if LDBL_PREC == 53
+#define        testall_tol     _testall_tol
+#else
+#define        testall_tol(prefix, x, result, tol, excepts) do {               \
+       _testall_tol(prefix, x, result, tol, excepts);                  \
+       test_tol(prefix##l, (x), (result),                              \
+                (tol) * ldexpl(1.0, 1 - LDBL_MANT_DIG), (excepts));    \
+} while (0)
+#endif
+
+#define testall(prefix, x, result, excepts)                            \
+       testall_tol(prefix, (x), (result), 0, (excepts))
+
+#define        test2_tol(func, y, x, result, tol, excepts) do {                \
+       volatile long double _iny = (y), _inx = (x), _out = (result);   \
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));                \
+       CHECK_FPEQUAL_TOL(func(_iny, _inx), _out, (tol), CS_BOTH);      \
+       CHECK_FP_EXCEPTIONS_MSG(excepts, ALL_STD_EXCEPT, "for %s(%s)",  \
+           #func, #x);                                                 \
+} while (0)
+#define test2(func, y, x, result, excepts)                             \
+       test2_tol(func, (y), (x), (result), 0, (excepts))
+
+#define        _testall2_tol(prefix, y, x, result, tol, excepts) do {          \
+       test2_tol(prefix, (double)(y), (double)(x), (double)(result),   \
+                 (tol) * ldexp(1.0, 1 - DBL_MANT_DIG), (excepts));     \
+       test2_tol(prefix##f, (float)(y), (float)(x), (float)(result),   \
+                 (tol) * ldexpf(1.0, 1 - FLT_MANT_DIG), (excepts));    \
+} while (0)
+
+#if LDBL_PREC == 53
+#define        testall2_tol    _testall2_tol
+#else
+#define        testall2_tol(prefix, y, x, result, tol, excepts) do {           \
+       _testall2_tol(prefix, y, x, result, tol, excepts);              \
+       test2_tol(prefix##l, (y), (x), (result),                        \
+                 (tol) * ldexpl(1.0, 1 - LDBL_MANT_DIG), (excepts));   \
+} while (0)
+#endif
+
+#define testall2(prefix, y, x, result, excepts)                                \
+       testall2_tol(prefix, (y), (x), (result), 0, (excepts))
+
+static long double
+pi =   3.14159265358979323846264338327950280e+00L,
+pio3 = 1.04719755119659774615421446109316766e+00L,
+c3pi = 9.42477796076937971538793014983850839e+00L,
+c7pi = 2.19911485751285526692385036829565196e+01L,
+c5pio3 = 5.23598775598298873077107230546583851e+00L,
+sqrt2m1 = 4.14213562373095048801688724209698081e-01L;
+
+
+/*
+ * Test special case inputs in asin(), acos() and atan(): signed
+ * zeroes, infinities, and NaNs.
+ */
+ATF_TC_WITHOUT_HEAD(special);
+ATF_TC_BODY(special, tc)
+{
+
+       testall(asin, 0.0, 0.0, 0);
+       testall(acos, 0.0, pi / 2, FE_INEXACT);
+       testall(atan, 0.0, 0.0, 0);
+       testall(asin, -0.0, -0.0, 0);
+       testall(acos, -0.0, pi / 2, FE_INEXACT);
+       testall(atan, -0.0, -0.0, 0);
+
+       testall(asin, INFINITY, NAN, FE_INVALID);
+       testall(acos, INFINITY, NAN, FE_INVALID);
+       testall(atan, INFINITY, pi / 2, FE_INEXACT);
+       testall(asin, -INFINITY, NAN, FE_INVALID);
+       testall(acos, -INFINITY, NAN, FE_INVALID);
+       testall(atan, -INFINITY, -pi / 2, FE_INEXACT);
+
+       testall(asin, NAN, NAN, 0);
+       testall(acos, NAN, NAN, 0);
+       testall(atan, NAN, NAN, 0);
+}
+
+/*
+ * Test special case inputs in atan2(), where the exact value of y/x is
+ * zero or non-finite.
+ */
+ATF_TC_WITHOUT_HEAD(special_atan2);
+ATF_TC_BODY(special_atan2, tc)
+{
+       long double z;
+       int e;
+
+       testall2(atan2, 0.0, -0.0, pi, FE_INEXACT);
+       testall2(atan2, -0.0, -0.0, -pi, FE_INEXACT);
+       testall2(atan2, 0.0, 0.0, 0.0, 0);
+       testall2(atan2, -0.0, 0.0, -0.0, 0);
+
+       testall2(atan2, INFINITY, -INFINITY, c3pi / 4, FE_INEXACT);
+       testall2(atan2, -INFINITY, -INFINITY, -c3pi / 4, FE_INEXACT);
+       testall2(atan2, INFINITY, INFINITY, pi / 4, FE_INEXACT);
+       testall2(atan2, -INFINITY, INFINITY, -pi / 4, FE_INEXACT);
+
+       /* Tests with one input in the range (0, Inf]. */
+       z = 1.23456789L;
+       for (e = FLT_MIN_EXP - FLT_MANT_DIG; e <= FLT_MAX_EXP; e++) {
+               test2(atan2f, 0.0, ldexpf(z, e), 0.0, 0);
+               test2(atan2f, -0.0, ldexpf(z, e), -0.0, 0);
+               test2(atan2f, 0.0, ldexpf(-z, e), (float)pi, FE_INEXACT);
+               test2(atan2f, -0.0, ldexpf(-z, e), (float)-pi, FE_INEXACT);
+               test2(atan2f, ldexpf(z, e), 0.0, (float)pi / 2, FE_INEXACT);
+               test2(atan2f, ldexpf(z, e), -0.0, (float)pi / 2, FE_INEXACT);
+               test2(atan2f, ldexpf(-z, e), 0.0, (float)-pi / 2, FE_INEXACT);
+               test2(atan2f, ldexpf(-z, e), -0.0, (float)-pi / 2, FE_INEXACT);
+       }
+       for (e = DBL_MIN_EXP - DBL_MANT_DIG; e <= DBL_MAX_EXP; e++) {
+               test2(atan2, 0.0, ldexp(z, e), 0.0, 0);
+               test2(atan2, -0.0, ldexp(z, e), -0.0, 0);
+               test2(atan2, 0.0, ldexp(-z, e), (double)pi, FE_INEXACT);
+               test2(atan2, -0.0, ldexp(-z, e), (double)-pi, FE_INEXACT);
+               test2(atan2, ldexp(z, e), 0.0, (double)pi / 2, FE_INEXACT);
+               test2(atan2, ldexp(z, e), -0.0, (double)pi / 2, FE_INEXACT);
+               test2(atan2, ldexp(-z, e), 0.0, (double)-pi / 2, FE_INEXACT);
+               test2(atan2, ldexp(-z, e), -0.0, (double)-pi / 2, FE_INEXACT);
+       }
+       for (e = LDBL_MIN_EXP - LDBL_MANT_DIG; e <= LDBL_MAX_EXP; e++) {
+               test2(atan2l, 0.0, ldexpl(z, e), 0.0, 0);
+               test2(atan2l, -0.0, ldexpl(z, e), -0.0, 0);
+               test2(atan2l, 0.0, ldexpl(-z, e), pi, FE_INEXACT);
+               test2(atan2l, -0.0, ldexpl(-z, e), -pi, FE_INEXACT);
+               test2(atan2l, ldexpl(z, e), 0.0, pi / 2, FE_INEXACT);
+               test2(atan2l, ldexpl(z, e), -0.0, pi / 2, FE_INEXACT);
+               test2(atan2l, ldexpl(-z, e), 0.0, -pi / 2, FE_INEXACT);
+               test2(atan2l, ldexpl(-z, e), -0.0, -pi / 2, FE_INEXACT);
+       }
+
+       /* Tests with one input in the range (0, Inf). */
+       for (e = FLT_MIN_EXP - FLT_MANT_DIG; e <= FLT_MAX_EXP - 1; e++) {
+               test2(atan2f, ldexpf(z, e), INFINITY, 0.0, 0);
+               test2(atan2f, ldexpf(-z,e), INFINITY, -0.0, 0);
+               test2(atan2f, ldexpf(z, e), -INFINITY, (float)pi, FE_INEXACT);
+               test2(atan2f, ldexpf(-z,e), -INFINITY, (float)-pi, FE_INEXACT);
+               test2(atan2f, INFINITY, ldexpf(z,e), (float)pi/2, FE_INEXACT);
+               test2(atan2f, INFINITY, ldexpf(-z,e), (float)pi/2, FE_INEXACT);
+               test2(atan2f, -INFINITY, ldexpf(z,e), (float)-pi/2,FE_INEXACT);
+               test2(atan2f, -INFINITY, ldexpf(-z,e),(float)-pi/2,FE_INEXACT);
+       }
+       for (e = DBL_MIN_EXP - DBL_MANT_DIG; e <= DBL_MAX_EXP - 1; e++) {
+               test2(atan2, ldexp(z, e), INFINITY, 0.0, 0);
+               test2(atan2, ldexp(-z,e), INFINITY, -0.0, 0);
+               test2(atan2, ldexp(z, e), -INFINITY, (double)pi, FE_INEXACT);
+               test2(atan2, ldexp(-z,e), -INFINITY, (double)-pi, FE_INEXACT);
+               test2(atan2, INFINITY, ldexp(z,e), (double)pi/2, FE_INEXACT);
+               test2(atan2, INFINITY, ldexp(-z,e), (double)pi/2, FE_INEXACT);
+               test2(atan2, -INFINITY, ldexp(z,e), (double)-pi/2,FE_INEXACT);
+               test2(atan2, -INFINITY, ldexp(-z,e),(double)-pi/2,FE_INEXACT);
+       }
+       for (e = LDBL_MIN_EXP - LDBL_MANT_DIG; e <= LDBL_MAX_EXP - 1; e++) {
+               test2(atan2l, ldexpl(z, e), INFINITY, 0.0, 0);
+               test2(atan2l, ldexpl(-z,e), INFINITY, -0.0, 0);
+               test2(atan2l, ldexpl(z, e), -INFINITY, pi, FE_INEXACT);
+               test2(atan2l, ldexpl(-z,e), -INFINITY, -pi, FE_INEXACT);
+               test2(atan2l, INFINITY, ldexpl(z, e), pi / 2, FE_INEXACT);
+               test2(atan2l, INFINITY, ldexpl(-z, e), pi / 2, FE_INEXACT);
+               test2(atan2l, -INFINITY, ldexpl(z, e), -pi / 2, FE_INEXACT);
+               test2(atan2l, -INFINITY, ldexpl(-z, e), -pi / 2, FE_INEXACT);
+       }
+}
+
+/*
+ * Test various inputs to asin(), acos() and atan() and verify that the
+ * results are accurate to within 1 ulp.
+ */
+ATF_TC_WITHOUT_HEAD(accuracy);
+ATF_TC_BODY(accuracy, tc)
+{
+
+       /* We expect correctly rounded results for these basic cases. */
+       testall(asin, 1.0, pi / 2, FE_INEXACT);
+       testall(acos, 1.0, 0, 0);
+       testall(atan, 1.0, pi / 4, FE_INEXACT);
+       testall(asin, -1.0, -pi / 2, FE_INEXACT);
+       testall(acos, -1.0, pi, FE_INEXACT);
+       testall(atan, -1.0, -pi / 4, FE_INEXACT);
+
+       /*
+        * Here we expect answers to be within 1 ulp, although inexactness
+        * in the input, combined with double rounding, could cause larger
+        * errors.
+        */
+
+       testall_tol(asin, sqrtl(2) / 2, pi / 4, 1, FE_INEXACT);
+       testall_tol(acos, sqrtl(2) / 2, pi / 4, 1, FE_INEXACT);
+       testall_tol(asin, -sqrtl(2) / 2, -pi / 4, 1, FE_INEXACT);
+       testall_tol(acos, -sqrtl(2) / 2, c3pi / 4, 1, FE_INEXACT);
+
+       testall_tol(asin, sqrtl(3) / 2, pio3, 1, FE_INEXACT);
+       testall_tol(acos, sqrtl(3) / 2, pio3 / 2, 1, FE_INEXACT);
+       testall_tol(atan, sqrtl(3), pio3, 1, FE_INEXACT);
+       testall_tol(asin, -sqrtl(3) / 2, -pio3, 1, FE_INEXACT);
+       testall_tol(acos, -sqrtl(3) / 2, c5pio3 / 2, 1, FE_INEXACT);
+       testall_tol(atan, -sqrtl(3), -pio3, 1, FE_INEXACT);
+
+       testall_tol(atan, sqrt2m1, pi / 8, 1, FE_INEXACT);
+       testall_tol(atan, -sqrt2m1, -pi / 8, 1, FE_INEXACT);
+}
+
+/*
+ * Test inputs to atan2() where x is a power of 2. These are easy cases
+ * because y/x is exact.
+ */
+ATF_TC_WITHOUT_HEAD(p2x_atan2);
+ATF_TC_BODY(p2x_atan2, tc)
+{
+
+       testall2(atan2, 1.0, 1.0, pi / 4, FE_INEXACT);
+       testall2(atan2, 1.0, -1.0, c3pi / 4, FE_INEXACT);
+       testall2(atan2, -1.0, 1.0, -pi / 4, FE_INEXACT);
+       testall2(atan2, -1.0, -1.0, -c3pi / 4, FE_INEXACT);
+
+       testall2_tol(atan2, sqrt2m1 * 2, 2.0, pi / 8, 1, FE_INEXACT);
+       testall2_tol(atan2, sqrt2m1 * 2, -2.0, c7pi / 8, 1, FE_INEXACT);
+       testall2_tol(atan2, -sqrt2m1 * 2, 2.0, -pi / 8, 1, FE_INEXACT);
+       testall2_tol(atan2, -sqrt2m1 * 2, -2.0, -c7pi / 8, 1, FE_INEXACT);
+
+       testall2_tol(atan2, sqrtl(3) * 0.5, 0.5, pio3, 1, FE_INEXACT);
+       testall2_tol(atan2, sqrtl(3) * 0.5, -0.5, pio3 * 2, 1, FE_INEXACT);
+       testall2_tol(atan2, -sqrtl(3) * 0.5, 0.5, -pio3, 1, FE_INEXACT);
+       testall2_tol(atan2, -sqrtl(3) * 0.5, -0.5, -pio3 * 2, 1, FE_INEXACT);
+}
+
+/*
+ * Test inputs very close to 0.
+ */
+ATF_TC_WITHOUT_HEAD(tiny);
+ATF_TC_BODY(tiny, tc)
+{
+       float tiny = 0x1.23456p-120f;
+
+       testall(asin, tiny, tiny, FE_INEXACT);
+       testall(acos, tiny, pi / 2, FE_INEXACT);
+       testall(atan, tiny, tiny, FE_INEXACT);
+
+       testall(asin, -tiny, -tiny, FE_INEXACT);
+       testall(acos, -tiny, pi / 2, FE_INEXACT);
+       testall(atan, -tiny, -tiny, FE_INEXACT);
+
+       /* Test inputs to atan2() that would cause y/x to underflow. */
+       test2(atan2f, 0x1.0p-100, 0x1.0p100, 0.0, FE_INEXACT | FE_UNDERFLOW);
+       test2(atan2, 0x1.0p-1000, 0x1.0p1000, 0.0, FE_INEXACT | FE_UNDERFLOW);
+       test2(atan2l, ldexpl(1.0, 100 - LDBL_MAX_EXP),
+             ldexpl(1.0, LDBL_MAX_EXP - 100), 0.0, FE_INEXACT | FE_UNDERFLOW);
+       test2(atan2f, -0x1.0p-100, 0x1.0p100, -0.0, FE_INEXACT | FE_UNDERFLOW);
+       test2(atan2, -0x1.0p-1000, 0x1.0p1000, -0.0, FE_INEXACT | FE_UNDERFLOW);
+       test2(atan2l, -ldexpl(1.0, 100 - LDBL_MAX_EXP),
+             ldexpl(1.0, LDBL_MAX_EXP - 100), -0.0, FE_INEXACT | FE_UNDERFLOW);
+       test2(atan2f, 0x1.0p-100, -0x1.0p100, (float)pi, FE_INEXACT);
+       test2(atan2, 0x1.0p-1000, -0x1.0p1000, (double)pi, FE_INEXACT);
+       test2(atan2l, ldexpl(1.0, 100 - LDBL_MAX_EXP),
+             -ldexpl(1.0, LDBL_MAX_EXP - 100), pi, FE_INEXACT);
+       test2(atan2f, -0x1.0p-100, -0x1.0p100, (float)-pi, FE_INEXACT);
+       test2(atan2, -0x1.0p-1000, -0x1.0p1000, (double)-pi, FE_INEXACT);
+       test2(atan2l, -ldexpl(1.0, 100 - LDBL_MAX_EXP),
+             -ldexpl(1.0, LDBL_MAX_EXP - 100), -pi, FE_INEXACT);
+}
+
+/*
+ * Test very large inputs to atan().
+ */
+ATF_TC_WITHOUT_HEAD(atan_huge);
+ATF_TC_BODY(atan_huge, tc)
+{
+       float huge = 0x1.23456p120;
+
+       testall(atan, huge, pi / 2, FE_INEXACT);
+       testall(atan, -huge, -pi / 2, FE_INEXACT);
+
+       /* Test inputs to atan2() that would cause y/x to overflow. */
+       test2(atan2f, 0x1.0p100, 0x1.0p-100, (float)pi / 2, FE_INEXACT);
+       test2(atan2, 0x1.0p1000, 0x1.0p-1000, (double)pi / 2, FE_INEXACT);
+       test2(atan2l, ldexpl(1.0, LDBL_MAX_EXP - 100),
+             ldexpl(1.0, 100 - LDBL_MAX_EXP), pi / 2, FE_INEXACT);
+       test2(atan2f, -0x1.0p100, 0x1.0p-100, (float)-pi / 2, FE_INEXACT);
+       test2(atan2, -0x1.0p1000, 0x1.0p-1000, (double)-pi / 2, FE_INEXACT);
+       test2(atan2l, -ldexpl(1.0, LDBL_MAX_EXP - 100),
+             ldexpl(1.0, 100 - LDBL_MAX_EXP), -pi / 2, FE_INEXACT);
+
+       test2(atan2f, 0x1.0p100, -0x1.0p-100, (float)pi / 2, FE_INEXACT);
+       test2(atan2, 0x1.0p1000, -0x1.0p-1000, (double)pi / 2, FE_INEXACT);
+       test2(atan2l, ldexpl(1.0, LDBL_MAX_EXP - 100),
+             -ldexpl(1.0, 100 - LDBL_MAX_EXP), pi / 2, FE_INEXACT);
+       test2(atan2f, -0x1.0p100, -0x1.0p-100, (float)-pi / 2, FE_INEXACT);
+       test2(atan2, -0x1.0p1000, -0x1.0p-1000, (double)-pi / 2, FE_INEXACT);
+       test2(atan2l, -ldexpl(1.0, LDBL_MAX_EXP - 100),
+             -ldexpl(1.0, 100 - LDBL_MAX_EXP), -pi / 2, FE_INEXACT);
+}
+
+/*
+ * Test that sin(asin(x)) == x, and similarly for acos() and atan().
+ * You need to have a working sinl(), cosl(), and tanl() for these
+ * tests to pass.
+ */
+static long double
+sinasinf(float x)
+{
+
+       return (sinl(asinf(x)));
+}
+
+static long double
+sinasin(double x)
+{
+
+       return (sinl(asin(x)));
+}
+
+static long double
+sinasinl(long double x)
+{
+
+       return (sinl(asinl(x)));
+}
+
+static long double
+cosacosf(float x)
+{
+
+       return (cosl(acosf(x)));
+}
+
+static long double
+cosacos(double x)
+{
+
+       return (cosl(acos(x)));
+}
+
+static long double
+cosacosl(long double x)
+{
+
+       return (cosl(acosl(x)));
+}
+
+static long double
+tanatanf(float x)
+{
+
+       return (tanl(atanf(x)));
+}
+
+static long double
+tanatan(double x)
+{
+
+       return (tanl(atan(x)));
+}
+
+static long double
+tanatanl(long double x)
+{
+
+       return (tanl(atanl(x)));
+}
+
+ATF_TC_WITHOUT_HEAD(inverse);
+ATF_TC_BODY(inverse, tc)
+{
+       float i;
+
+       for (i = -1; i <= 1; i += 0x1.0p-12f) {
+               testall_tol(sinasin, i, i, 2, i == 0 ? 0 : FE_INEXACT);
+               /* The relative error for cosacos is very large near x=0. */
+               if (fabsf(i) > 0x1.0p-4f)
+                       testall_tol(cosacos, i, i, 16, i == 1 ? 0 : FE_INEXACT);
+               testall_tol(tanatan, i, i, 2, i == 0 ? 0 : FE_INEXACT);
+       }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, special);
+       ATF_TP_ADD_TC(tp, special_atan2);
+       ATF_TP_ADD_TC(tp, accuracy);
+       ATF_TP_ADD_TC(tp, p2x_atan2);
+       ATF_TP_ADD_TC(tp, tiny);
+       ATF_TP_ADD_TC(tp, atan_huge);
+       ATF_TP_ADD_TC(tp, inverse);
+
+       return (atf_no_error());
+}
diff --git a/regress/lib/libm/msun/logarithm_test.c b/regress/lib/libm/msun/logarithm_test.c
new file mode 100644 (file)
index 0000000..2263c81
--- /dev/null
@@ -0,0 +1,283 @@
+/*     $OpenBSD: logarithm_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $        */
+/*-
+ * Copyright (c) 2008-2010 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Tests for corner cases in log*().
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+#ifdef __i386__
+#include <ieeefp.h>
+#endif
+
+#include "test-utils.h"
+
+#pragma STDC FENV_ACCESS ON
+
+/*
+ * Test that a function returns the correct value and sets the
+ * exception flags correctly. The exceptmask specifies which
+ * exceptions we should check. We need to be lenient for several
+ * reasoons, but mainly because on some architectures it's impossible
+ * to raise FE_OVERFLOW without raising FE_INEXACT.
+ *
+ * These are macros instead of functions so that assert provides more
+ * meaningful error messages.
+ *
+ * XXX The volatile here is to avoid gcc's bogus constant folding and work
+ *     around the lack of support for the FENV_ACCESS pragma.
+ */
+#define        test(func, x, result, exceptmask, excepts)      do { \
+       volatile long double _d = x;                           \
+       ATF_CHECK_EQ(0, feclearexcept(FE_ALL_EXCEPT));                  \
+       CHECK_FPEQUAL((func)(_d), (result));                    \
+       CHECK_FP_EXCEPTIONS_MSG(excepts, exceptmask, "for %s(%s)",      \
+           #func, #x);                                                 \
+} while (0)
+
+#define        test_tol(func, z, result, tol)                  do {            \
+       volatile long double _d = z;                                    \
+       debug("  testing %6s(%15La) ~= % .36Le\n", #func, _d, result);  \
+       CHECK_FPEQUAL_TOL((func)(_d), (result), (tol), CS_BOTH);        \
+} while (0)
+
+/* Test all the functions that compute log(x). */
+#define        testall0(x, result, exceptmask, excepts)        do {            \
+       test(log, x, result, exceptmask, excepts);                      \
+       test(logf, x, result, exceptmask, excepts);                     \
+       test(logl, x, result, exceptmask, excepts);                     \
+       test(log2, x, result, exceptmask, excepts);                     \
+       test(log2f, x, result, exceptmask, excepts);                    \
+       test(log2l, x, result, exceptmask, excepts);                    \
+       test(log10, x, result, exceptmask, excepts);                    \
+       test(log10f, x, result, exceptmask, excepts);                   \
+       test(log10l, x, result, exceptmask, excepts);                   \
+} while (0)
+
+/* Test all the functions that compute log(1+x). */
+#define        testall1(x, result, exceptmask, excepts)        do {            \
+       test(log1p, x, result, exceptmask, excepts);                    \
+       test(log1pf, x, result, exceptmask, excepts);                   \
+       test(log1pl, x, result, exceptmask, excepts);                   \
+} while (0)
+
+ATF_TC_WITHOUT_HEAD(generic_tests);
+ATF_TC_BODY(generic_tests, tc)
+{
+
+       /* log(1) == 0, no exceptions raised */
+       testall0(1.0, 0.0, ALL_STD_EXCEPT, 0);
+       testall1(0.0, 0.0, ALL_STD_EXCEPT, 0);
+       testall1(-0.0, -0.0, ALL_STD_EXCEPT, 0);
+
+       /* log(NaN) == NaN, no exceptions raised */
+       testall0(NAN, NAN, ALL_STD_EXCEPT, 0);
+       testall1(NAN, NAN, ALL_STD_EXCEPT, 0);
+
+       /* log(Inf) == Inf, no exceptions raised */
+       testall0(INFINITY, INFINITY, ALL_STD_EXCEPT, 0);
+       testall1(INFINITY, INFINITY, ALL_STD_EXCEPT, 0);
+
+       /* log(x) == NaN for x < 0, invalid exception raised */
+       testall0(-INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID);
+       testall1(-INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID);
+       testall0(-1.0, NAN, ALL_STD_EXCEPT, FE_INVALID);
+       testall1(-1.5, NAN, ALL_STD_EXCEPT, FE_INVALID);
+
+       /* log(0) == -Inf, divide-by-zero exception */
+       testall0(0.0, -INFINITY, ALL_STD_EXCEPT & ~FE_INEXACT, FE_DIVBYZERO);
+       testall0(-0.0, -INFINITY, ALL_STD_EXCEPT & ~FE_INEXACT, FE_DIVBYZERO);
+       testall1(-1.0, -INFINITY, ALL_STD_EXCEPT & ~FE_INEXACT, FE_DIVBYZERO);
+}
+
+ATF_TC_WITHOUT_HEAD(log2_tests);
+ATF_TC_BODY(log2_tests, tc)
+{
+       unsigned i;
+
+       /*
+        * We should insist that log2() return exactly the correct
+        * result and not raise an inexact exception for powers of 2.
+        */
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+       for (i = FLT_MIN_EXP - FLT_MANT_DIG; i < FLT_MAX_EXP; i++) {
+               ATF_CHECK_EQ(i, log2f(ldexpf(1.0, i)));
+               CHECK_FP_EXCEPTIONS(0, ALL_STD_EXCEPT);
+       }
+       for (i = DBL_MIN_EXP - DBL_MANT_DIG; i < DBL_MAX_EXP; i++) {
+               ATF_CHECK_EQ(i, log2(ldexp(1.0, i)));
+               CHECK_FP_EXCEPTIONS(0, ALL_STD_EXCEPT);
+       }
+       for (i = LDBL_MIN_EXP - LDBL_MANT_DIG; i < LDBL_MAX_EXP; i++) {
+               ATF_CHECK_EQ(i, log2l(ldexpl(1.0, i)));
+               CHECK_FP_EXCEPTIONS(0, ALL_STD_EXCEPT);
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(roundingmode_tests);
+ATF_TC_BODY(roundingmode_tests, tc)
+{
+
+       /*
+        * Corner cases in other rounding modes.
+        */
+       fesetround(FE_DOWNWARD);
+       /* These are still positive per IEEE 754R */
+#if 0
+       testall0(1.0, 0.0, ALL_STD_EXCEPT, 0);
+#else
+       /* logl, log2l, and log10l don't pass yet. */
+       test(log, 1.0, 0.0, ALL_STD_EXCEPT, 0);
+       test(logf, 1.0, 0.0, ALL_STD_EXCEPT, 0);
+       test(log2, 1.0, 0.0, ALL_STD_EXCEPT, 0);
+       test(log2f, 1.0, 0.0, ALL_STD_EXCEPT, 0);
+       test(log10, 1.0, 0.0, ALL_STD_EXCEPT, 0);
+       test(log10f, 1.0, 0.0, ALL_STD_EXCEPT, 0);
+#endif
+       testall1(0.0, 0.0, ALL_STD_EXCEPT, 0);
+       fesetround(FE_TOWARDZERO);
+       testall0(1.0, 0.0, ALL_STD_EXCEPT, 0);
+       testall1(0.0, 0.0, ALL_STD_EXCEPT, 0);
+
+       fesetround(FE_UPWARD);
+       testall0(1.0, 0.0, ALL_STD_EXCEPT, 0);
+       testall1(0.0, 0.0, ALL_STD_EXCEPT, 0);
+       /* log1p(-0.0) == -0.0 even when rounding upwards */
+       testall1(-0.0, -0.0, ALL_STD_EXCEPT, 0);
+
+       fesetround(FE_TONEAREST);
+}
+
+ATF_TC_WITHOUT_HEAD(accuracy_tests);
+ATF_TC_BODY(accuracy_tests, tc)
+{
+       static const struct {
+               float x;
+               long double log2x;
+               long double logex;
+               long double log10x;
+        } tests[] = {
+               {  0x1p-120 + 0x1p-140,
+                 -1.19999998624139449158861798943319717e2L,
+                 -8.31776607135195754708796206665656732e1L,
+                 -3.61235990655024477716980559136055915e1L,
+               },
+               {  1.0 - 0x1p-20,
+                 -1.37586186296463416424364914705656460e-6L,
+                 -9.53674771153890007250243736279163253e-7L,
+                 -4.14175690642480911859354110516159131e-7L, },
+               {  1.0 + 0x1p-20,
+                  1.37586055084113820105668028340371476e-6L,
+                  9.53673861659188233908415514963336144e-7L,
+                  4.14175295653950611453333571759200697e-7L },
+               {  19.75,
+                  4.30378074817710292442728634194115348e0L,
+                  2.98315349134713087533848129856505779e0L,
+                  1.29556709996247903756734359702926363e0L },
+               {  19.75 * 0x1p100,
+                  1.043037807481771029244272863419411534e2L,
+                  72.29787154734166181706169344438271459357255439172762452L,
+                  3.139856666636059855894123306947856631e1L },
+       };
+        unsigned i;
+
+       long double log1p_ldbl_ulp = LDBL_ULP();
+#if LDBL_MANT_DIG > 64
+       /*
+        * On ld128 platforms the log1p() implementation provides less accuracy,
+        * but does still match the ld80 precision. Use the ld80 LDBL_ULP()
+        * value for now to avoid losing test coverage for the other functions.
+        * Reported as https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=253984
+        */
+       log1p_ldbl_ulp = ldexpl(1.0, 1 - 64);
+#endif
+
+       for (i = 0; i < nitems(tests); i++) {
+               test_tol(log2, tests[i].x, tests[i].log2x, DBL_ULP());
+               test_tol(log2f, tests[i].x, tests[i].log2x, FLT_ULP());
+               test_tol(log2l, tests[i].x, tests[i].log2x, LDBL_ULP());
+               test_tol(log, tests[i].x, tests[i].logex, DBL_ULP());
+               test_tol(logf, tests[i].x, tests[i].logex, FLT_ULP());
+               test_tol(logl, tests[i].x, tests[i].logex, LDBL_ULP());
+               test_tol(log10, tests[i].x, tests[i].log10x, DBL_ULP());
+               test_tol(log10f, tests[i].x, tests[i].log10x, FLT_ULP());
+               test_tol(log10l, tests[i].x, tests[i].log10x, LDBL_ULP());
+               if (tests[i].x >= 0.5) {
+                       test_tol(log1p, tests[i].x - 1, tests[i].logex,
+                                DBL_ULP());
+                       test_tol(log1pf, tests[i].x - 1, tests[i].logex,
+                                FLT_ULP());
+                       test_tol(log1pl, tests[i].x - 1, tests[i].logex,
+                                log1p_ldbl_ulp);
+               }
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(log1p_accuracy_tests);
+ATF_TC_BODY(log1p_accuracy_tests, tc)
+{
+#if LDBL_MANT_DIG > 64
+       if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false))
+               atf_tc_expect_fail("https://bugs.freebsd.org/253984");
+#endif
+
+       test_tol(log1pf, 0x0.333333p0F,
+                1.82321546859847114303367992804596800640e-1L, FLT_ULP());
+       test_tol(log1p, 0x0.3333333333333p0,
+                1.82321556793954589204283870982629267635e-1L, DBL_ULP());
+       test_tol(log1pl, 0x0.33333333333333332p0L,
+                1.82321556793954626202683007050468762914e-1L, LDBL_ULP());
+
+       test_tol(log1pf, -0x0.333333p0F,
+                -2.23143536413048672940940199918017467652e-1L, FLT_ULP());
+       test_tol(log1p, -0x0.3333333333333p0,
+                -2.23143551314209700255143859052009022937e-1L, DBL_ULP());
+       test_tol(log1pl, -0x0.33333333333333332p0L,
+                -2.23143551314209755752742563153765697950e-1L, LDBL_ULP());
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+       ATF_TP_ADD_TC(tp, generic_tests);
+       ATF_TP_ADD_TC(tp, log2_tests);
+       ATF_TP_ADD_TC(tp, roundingmode_tests);
+       ATF_TP_ADD_TC(tp, accuracy_tests);
+       ATF_TP_ADD_TC(tp, log1p_accuracy_tests);
+
+       return (atf_no_error());
+}
index fa1b173..5a1e281 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: lrint_test.c,v 1.3 2019/02/21 18:14:16 bluhm Exp $    */
+/*     $OpenBSD: lrint_test.c,v 1.4 2021/10/22 18:00:22 mbuhl Exp $    */
 /*-
  * Copyright (c) 2005-2008 David Schultz <das@FreeBSD.org>
  * All rights reserved.
 #include <sys/cdefs.h>
 /* $FreeBSD: head/lib/msun/tests/lrint_test.c 314650 2017-03-04 10:07:46Z ngie $ */
 
-#include <assert.h>
 #include <fenv.h>
 #include <limits.h>
 #include <math.h>
 #include <stdio.h>
 
-/*
- * XXX The volatile here is to avoid gcc's bogus constant folding and work
- *     around the lack of support for the FENV_ACCESS pragma.
- */
-#define        test(func, x, result, excepts)  do {                            \
-       volatile double _d = x;                                         \
-       assert(feclearexcept(FE_ALL_EXCEPT) == 0);                      \
-       assert((func)(_d) == (result) || fetestexcept(FE_INVALID));     \
-       assert(fetestexcept(FE_ALL_EXCEPT) == (excepts));               \
+#include "test-utils.h"
+
+#define        test(func, x, result, excepts)  do {                                    \
+       ATF_CHECK(feclearexcept(FE_ALL_EXCEPT) == 0);                           \
+       long long _r = (func)(x);                                               \
+       ATF_CHECK_MSG(_r == (result) || fetestexcept(FE_INVALID),               \
+           #func "(%Lg) returned %lld, expected %lld", (long double)x, _r,     \
+           (long long)(result));                                               \
+       CHECK_FP_EXCEPTIONS_MSG(excepts, FE_ALL_EXCEPT & ALL_STD_EXCEPT,        \
+           "for %s(%s)", #func, #x);                                           \
 } while (0)
 
 #define        testall(x, result, excepts)     do {                            \
 static void
 run_tests(void)
 {
-
-       assert(fesetround(FE_DOWNWARD) == 0);
+       ATF_REQUIRE_EQ(0, fesetround(FE_DOWNWARD));
        testall(0.75, 0, FE_INEXACT);
        testall(-0.5, -1, FE_INEXACT);
 
-       assert(fesetround(FE_TONEAREST) == 0);
+       ATF_REQUIRE_EQ(0, fesetround(FE_TONEAREST));
        testall(0.0, 0, 0);
        testall(0.25, 0, FE_INEXACT);
        testall(0.5, 0, FE_INEXACT);
@@ -82,61 +81,66 @@ run_tests(void)
        testall(NAN, IGNORE, FE_INVALID);
 
 #if (LONG_MAX == 0x7fffffffl)
-       assert(fesetround(FE_UPWARD) == 0);
+       ATF_REQUIRE_EQ(0, fesetround(FE_UPWARD));
        test(lrint, 0x7fffffff.8p0, IGNORE, FE_INVALID);
-       test(lrint, -0x80000000.4p0, -0x80000000l, FE_INEXACT);
+       test(lrint, -0x80000000.4p0, (long)-0x80000000l, FE_INEXACT);
 
-       assert(fesetround(FE_DOWNWARD) == 0);
+       ATF_REQUIRE_EQ(0, fesetround(FE_DOWNWARD));
        test(lrint, -0x80000000.8p0, IGNORE, FE_INVALID);
        test(lrint, 0x80000000.0p0, IGNORE, FE_INVALID);
        test(lrint, 0x7fffffff.4p0, 0x7fffffffl, FE_INEXACT);
        test(lrintf, 0x80000000.0p0f, IGNORE, FE_INVALID);
        test(lrintf, 0x7fffff80.0p0f, 0x7fffff80l, 0);
 
-       assert(fesetround(FE_TOWARDZERO) == 0);
+       ATF_REQUIRE_EQ(0, fesetround(FE_TOWARDZERO));
        test(lrint, 0x7fffffff.8p0,  0x7fffffffl, FE_INEXACT);
        test(lrint, -0x80000000.8p0, -0x80000000l, FE_INEXACT);
        test(lrint, 0x80000000.0p0, IGNORE, FE_INVALID);
        test(lrintf, 0x80000000.0p0f, IGNORE, FE_INVALID);
        test(lrintf, 0x7fffff80.0p0f, 0x7fffff80l, 0);
 #elif (LONG_MAX == 0x7fffffffffffffffll)
-       assert(fesetround(FE_TONEAREST) == 0);
+       ATF_REQUIRE_EQ(0, fesetround(FE_TONEAREST));
        test(lrint, 0x8000000000000000.0p0, IGNORE, FE_INVALID);
        test(lrintf, 0x8000000000000000.0p0f, IGNORE, FE_INVALID);
        test(lrint, 0x7ffffffffffffc00.0p0, 0x7ffffffffffffc00l, 0);
        test(lrintf, 0x7fffff8000000000.0p0f, 0x7fffff8000000000l, 0);
        test(lrint, -0x8000000000000800.0p0, IGNORE, FE_INVALID);
        test(lrintf, -0x8000010000000000.0p0f, IGNORE, FE_INVALID);
-       test(lrint, -0x8000000000000000.0p0, -0x8000000000000000l, 0);
-       test(lrintf, -0x8000000000000000.0p0f, -0x8000000000000000l, 0);
+       test(lrint, -0x8000000000000000.0p0, (long long)-0x8000000000000000ul, 0);
+       test(lrintf, -0x8000000000000000.0p0f, (long long)-0x8000000000000000ul, 0);
 #else
 #error "Unsupported long size"
 #endif
 
 #if (LLONG_MAX == 0x7fffffffffffffffLL)
-       assert(fesetround(FE_TONEAREST) == 0);
+       ATF_REQUIRE_EQ(0, fesetround(FE_TONEAREST));
        test(llrint, 0x8000000000000000.0p0, IGNORE, FE_INVALID);
        test(llrintf, 0x8000000000000000.0p0f, IGNORE, FE_INVALID);
        test(llrint, 0x7ffffffffffffc00.0p0, 0x7ffffffffffffc00ll, 0);
        test(llrintf, 0x7fffff8000000000.0p0f, 0x7fffff8000000000ll, 0);
        test(llrint, -0x8000000000000800.0p0, IGNORE, FE_INVALID);
        test(llrintf, -0x8000010000000000.0p0f, IGNORE, FE_INVALID);
-       test(llrint, -0x8000000000000000.0p0, -0x8000000000000000ll, 0);
-       test(llrintf, -0x8000000000000000.0p0f, -0x8000000000000000ll, 0);
+       test(llrint, -0x8000000000000000.0p0, (long long)-0x8000000000000000ull, 0);
+       test(llrintf, -0x8000000000000000.0p0f, (long long)-0x8000000000000000ull, 0);
 #else
 #error "Unsupported long long size"
 #endif
 }
 
-int
-main(void)
+ATF_TC_WITHOUT_HEAD(lrint);
+ATF_TC_BODY(lrint, tc)
 {
-
-       printf("1..1\n");
-
        run_tests();
+#ifndef __OpenBSD__
+#ifdef __i386__
+       fpsetprec(FP_PE);
+       run_tests();
+#endif
+#endif
+}
 
-       printf("ok 1 - lrint\n");
-
-       return (0);
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, lrint);
+       return (atf_no_error());
 }
diff --git a/regress/lib/libm/msun/lround_test.c b/regress/lib/libm/msun/lround_test.c
new file mode 100644 (file)
index 0000000..9f83b09
--- /dev/null
@@ -0,0 +1,123 @@
+/*     $OpenBSD: lround_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $   */
+/*-
+ * Copyright (c) 2005 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Test for lround(), lroundf(), llround(), and llroundf().
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <fenv.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "test-utils.h"
+
+#define        IGNORE  0x12345
+
+#define        test(func, x, result, excepts)  do {                                    \
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));                        \
+       long long _r = (func)(x);                                               \
+       CHECK_FP_EXCEPTIONS_MSG(excepts, FE_ALL_EXCEPT, "for %s(%s)",           \
+           #func, #x);                                                         \
+       if ((excepts & FE_INVALID) != 0) {                                      \
+               ATF_REQUIRE_EQ(result, IGNORE);                                 \
+               ATF_CHECK_EQ_MSG(FE_INVALID, fetestexcept(FE_INVALID),          \
+                   "FE_INVALID not set correctly for %s(%s)", #func, #x);      \
+       } else {                                                                \
+               ATF_REQUIRE_MSG(result != IGNORE, "Expected can't be IGNORE!"); \
+               ATF_REQUIRE_EQ(result, (__STRING(func(_d)), _r));               \
+       }                                                                       \
+} while (0)
+
+#define        testall(x, result, excepts)     do {                            \
+       test(lround, x, result, excepts);                               \
+       test(lroundf, x, result, excepts);                              \
+       test(llround, x, result, excepts);                              \
+       test(llroundf, x, result, excepts);                             \
+} while (0)
+
+#pragma STDC FENV_ACCESS ON
+
+ATF_TC_WITHOUT_HEAD(main);
+ATF_TC_BODY(main, tc)
+{
+       testall(0.0, 0, 0);
+       testall(0.25, 0, FE_INEXACT);
+       testall(0.5, 1, FE_INEXACT);
+       testall(-0.5, -1, FE_INEXACT);
+       testall(1.0, 1, 0);
+       testall(0x12345000p0, 0x12345000, 0);
+       testall(0x1234.fp0, 0x1235, FE_INEXACT);
+       testall(INFINITY, IGNORE, FE_INVALID);
+       testall(NAN, IGNORE, FE_INVALID);
+
+#if (LONG_MAX == 0x7fffffffl)
+       test(lround, 0x7fffffff.8p0, IGNORE, FE_INVALID);
+       test(lround, -0x80000000.8p0, IGNORE, FE_INVALID);
+       test(lround, 0x80000000.0p0, IGNORE, FE_INVALID);
+       test(lround, 0x7fffffff.4p0, 0x7fffffffl, FE_INEXACT);
+       test(lround, -0x80000000.4p0, -0x80000000l, FE_INEXACT);
+       test(lroundf, 0x80000000.0p0f, IGNORE, FE_INVALID);
+       test(lroundf, 0x7fffff80.0p0f, 0x7fffff80l, 0);
+#elif (LONG_MAX == 0x7fffffffffffffffll)
+       test(lround, 0x8000000000000000.0p0, IGNORE, FE_INVALID);
+       test(lroundf, 0x8000000000000000.0p0f, IGNORE, FE_INVALID);
+       test(lround, 0x7ffffffffffffc00.0p0, 0x7ffffffffffffc00l, 0);
+       test(lroundf, 0x7fffff8000000000.0p0f, 0x7fffff8000000000l, 0);
+       test(lround, -0x8000000000000800.0p0, IGNORE, FE_INVALID);
+       test(lroundf, -0x8000010000000000.0p0f, IGNORE, FE_INVALID);
+       test(lround, -0x8000000000000000.0p0, (long)-0x8000000000000000l, 0);
+       test(lroundf, -0x8000000000000000.0p0f, (long)-0x8000000000000000l, 0);
+#else
+#error "Unsupported long size"
+#endif
+
+#if (LLONG_MAX == 0x7fffffffffffffffLL)
+       test(llround, 0x8000000000000000.0p0, IGNORE, FE_INVALID);
+       test(llroundf, 0x8000000000000000.0p0f, IGNORE, FE_INVALID);
+       test(llround, 0x7ffffffffffffc00.0p0, 0x7ffffffffffffc00ll, 0);
+       test(llroundf, 0x7fffff8000000000.0p0f, 0x7fffff8000000000ll, 0);
+       test(llround, -0x8000000000000800.0p0, IGNORE, FE_INVALID);
+       test(llroundf, -0x8000010000000000.0p0f, IGNORE, FE_INVALID);
+       test(llround, -0x8000000000000000.0p0, (long long)-0x8000000000000000ll, 0);
+       test(llroundf, -0x8000000000000000.0p0f, (long long)-0x8000000000000000ll, 0);
+#else
+#error "Unsupported long long size"
+#endif
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, main);
+
+       return (atf_no_error());
+}
diff --git a/regress/lib/libm/msun/macros.h b/regress/lib/libm/msun/macros.h
new file mode 100644 (file)
index 0000000..ed682c2
--- /dev/null
@@ -0,0 +1,7 @@
+/*      $OpenBSD: macros.h,v 1.1 2021/10/22 18:00:22 mbuhl Exp $       */
+/* Public domain - Moritz Buhl */
+
+#define __FBSDID(a)
+#define rounddown(x, y)        (((x)/(y))*(y))
+#define fpequal(a, b)  fpequal_cs(a, b, 1)
+#define hexdump(...)
diff --git a/regress/lib/libm/msun/nan_test.c b/regress/lib/libm/msun/nan_test.c
new file mode 100644 (file)
index 0000000..833290d
--- /dev/null
@@ -0,0 +1,126 @@
+/*     $OpenBSD: nan_test.c,v 1.1 2021/10/22 18:00:23 mbuhl Exp $      */
+/*-
+ * Copyright (C) 2007 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Test for nan(), nanf(), and nanl(). We also test that strtod("nan(...)")
+ * and sscanf("nan(...)", ...) work identically.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <fenv.h>
+#include <float.h>
+#include <locale.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "test-utils.h"
+
+static void
+testnan(const char *nan_format)
+{
+       char nan_str[128];
+       char *end;
+       long double ald[4];
+       double ad[4];
+       float af[4];
+       unsigned i;
+
+       snprintf(nan_str, sizeof(nan_str), "nan(%s)", nan_format);
+       for (i = 0; i < nitems(ad); i++) {
+               /*
+                * x86 has an 80-bit long double stored in 96 bits,
+                * so we need to initialize the memory for the memcmp()
+                * checks below to work.
+                */
+               bzero(&af[i], sizeof(float));
+               bzero(&ad[i], sizeof(double));
+               bzero(&ald[i], sizeof(long double));
+       }
+
+       af[0] = nanf(nan_format);
+       ATF_REQUIRE(isnan(af[0]));
+       af[1] = strtof(nan_str, &end);
+       ATF_REQUIRE(end == nan_str + strlen(nan_str));
+       ATF_REQUIRE(sscanf(nan_str, "%e", &af[2]) == 1);
+       ATF_REQUIRE(memcmp(&af[0], &af[1], sizeof(float)) == 0);
+       ATF_REQUIRE(memcmp(&af[1], &af[2], sizeof(float)) == 0);
+       if (*nan_format == '\0') {
+               /* nanf("") == strtof("nan") */
+               af[3] = strtof("nan", NULL);
+               ATF_REQUIRE(memcmp(&af[2], &af[3], sizeof(float)) == 0);
+       }
+
+       ad[0] = nan(nan_format);
+       ATF_REQUIRE(isnan(ad[0]));
+       ad[1] = strtod(nan_str, &end);
+       ATF_REQUIRE(end == nan_str + strlen(nan_str));
+       ATF_REQUIRE(sscanf(nan_str, "%le", &ad[2]) == 1);
+       ATF_REQUIRE(memcmp(&ad[0], &ad[1], sizeof(double)) == 0);
+       ATF_REQUIRE(memcmp(&ad[1], &ad[2], sizeof(double)) == 0);
+       if (*nan_format == '\0') {
+               /* nan("") == strtod("nan") */
+               ad[3] = strtod("nan", NULL);
+               ATF_REQUIRE(memcmp(&ad[2], &ad[3], sizeof(double)) == 0);
+       }
+
+       ald[0] = nanl(nan_format);
+       ATF_REQUIRE(isnan(ald[0]));
+       ald[1] = strtold(nan_str, &end);
+       ATF_REQUIRE(end == nan_str + strlen(nan_str));
+       ATF_REQUIRE(sscanf(nan_str, "%Le", &ald[2]) == 1);
+       ATF_REQUIRE(memcmp(&ald[0], &ald[1], sizeof(long double)) == 0);
+       ATF_REQUIRE(memcmp(&ald[1], &ald[2], sizeof(long double)) == 0);
+       if (*nan_format == '\0') {
+               /* nanl("") == strtold("nan") */
+               ald[3] = strtold("nan", NULL);
+               ATF_REQUIRE(memcmp(&ald[2], &ald[3], sizeof(long double)) == 0);
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(nan);
+ATF_TC_BODY(nan, tc)
+{
+       /* Die if a signalling NaN is returned */
+       feenableexcept(FE_INVALID);
+
+       testnan("0x1234");
+       testnan("");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, nan);
+
+       return (atf_no_error());
+}
diff --git a/regress/lib/libm/msun/nearbyint_test.c b/regress/lib/libm/msun/nearbyint_test.c
new file mode 100644 (file)
index 0000000..35ec372
--- /dev/null
@@ -0,0 +1,174 @@
+/*     $OpenBSD: nearbyint_test.c,v 1.1 2021/10/22 18:00:23 mbuhl Exp $        */
+/*-
+ * Copyright (c) 2010 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Tests for nearbyint{,f,l}()
+ *
+ * TODO:
+ * - adapt tests for rint(3)
+ * - tests for harder values (more mantissa bits than float)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <fenv.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "test-utils.h"
+
+static const int rmodes[] = {
+       FE_TONEAREST, FE_DOWNWARD, FE_UPWARD, FE_TOWARDZERO,
+};
+
+/* Make sure we're testing the library, not some broken compiler built-ins. */
+static double (*libnearbyint)(double) = nearbyint;
+static float (*libnearbyintf)(float) = nearbyintf;
+static long double (*libnearbyintl)(long double) = nearbyintl;
+#define nearbyintf libnearbyintf
+#define nearbyint libnearbyint
+#define nearbyintl libnearbyintl
+
+static const struct {
+       float in;
+       float out[3];   /* one answer per rounding mode except towardzero */
+} tests[] = {
+/* input       output (expected) */
+    { 0.0,     { 0.0, 0.0, 0.0 }},
+    { 0.5,     { 0.0, 0.0, 1.0 }},
+    { M_PI,    { 3.0, 3.0, 4.0 }},
+    { 65536.5, { 65536, 65536, 65537 }},
+    { INFINITY,        { INFINITY, INFINITY, INFINITY }},
+    { NAN,     { NAN, NAN, NAN }},
+};
+
+/* Get the appropriate result for the current rounding mode. */
+static float
+get_output(int testindex, int rmodeindex, int negative)
+{
+       double out;
+
+       if (negative) { /* swap downwards and upwards if input is negative */
+               if (rmodeindex == 1)
+                       rmodeindex = 2;
+               else if (rmodeindex == 2)
+                       rmodeindex = 1;
+       }
+       if (rmodeindex == 3) /* FE_TOWARDZERO uses the value for downwards */
+               rmodeindex = 1;
+       out = tests[testindex].out[rmodeindex];
+       return (negative ? -out : out);
+}
+
+static void
+test_nearby(int testindex)
+{
+       float in, out;
+       unsigned i;
+
+       for (i = 0; i < sizeof(rmodes) / sizeof(rmodes[0]); i++) {
+               ATF_REQUIRE_EQ(0, fesetround(rmodes[i]));
+               ATF_REQUIRE_EQ(0, feclearexcept(ALL_STD_EXCEPT));
+
+               in = tests[testindex].in;
+               out = get_output(testindex, i, 0);
+               CHECK_FPEQUAL(out, libnearbyintf(in));
+               CHECK_FPEQUAL(out, nearbyint(in));
+               CHECK_FPEQUAL(out, nearbyintl(in));
+               CHECK_FP_EXCEPTIONS(0, ALL_STD_EXCEPT);
+
+               in = -tests[testindex].in;
+               out = get_output(testindex, i, 1);
+               CHECK_FPEQUAL(out, nearbyintf(in));
+               CHECK_FPEQUAL(out, nearbyint(in));
+               CHECK_FPEQUAL(out, nearbyintl(in));
+               CHECK_FP_EXCEPTIONS(0, ALL_STD_EXCEPT);
+       }
+}
+
+static void
+test_modf(int testindex)
+{
+       float in, out;
+       float ipartf, ipart_expected;
+       double ipart;
+       long double ipartl;
+       unsigned i;
+
+       for (i = 0; i < sizeof(rmodes) / sizeof(rmodes[0]); i++) {
+               ATF_REQUIRE_EQ(0, fesetround(rmodes[i]));
+               ATF_REQUIRE_EQ(0, feclearexcept(ALL_STD_EXCEPT));
+
+               in = tests[testindex].in;
+               ipart_expected = tests[testindex].out[1];
+               out = copysignf(
+                   isinf(ipart_expected) ? 0.0 : in - ipart_expected, in);
+               ipartl = ipart = ipartf = 42.0;
+
+               CHECK_FPEQUAL(out, modff(in, &ipartf));
+               CHECK_FPEQUAL(ipart_expected, ipartf);
+               CHECK_FPEQUAL(out, modf(in, &ipart));
+               CHECK_FPEQUAL(ipart_expected, ipart);
+               CHECK_FPEQUAL(out, modfl(in, &ipartl));
+               CHECK_FPEQUAL(ipart_expected, ipartl);
+               CHECK_FP_EXCEPTIONS(0, ALL_STD_EXCEPT);
+
+               in = -in;
+               ipart_expected = -ipart_expected;
+               out = -out;
+               ipartl = ipart = ipartf = 42.0;
+               CHECK_FPEQUAL(out, modff(in, &ipartf));
+               CHECK_FPEQUAL(ipart_expected, ipartf);
+               CHECK_FPEQUAL(out, modf(in, &ipart));
+               CHECK_FPEQUAL(ipart_expected, ipart);
+               CHECK_FPEQUAL(out, modfl(in, &ipartl));
+               CHECK_FPEQUAL(ipart_expected, ipartl);
+               CHECK_FP_EXCEPTIONS(0, ALL_STD_EXCEPT);
+       }
+}
+
+ATF_TC_WITHOUT_HEAD(nearbyint);
+ATF_TC_BODY(nearbyint, tc)
+{
+       unsigned i;
+
+       for (i = 0; i < nitems(tests); i++) {
+               test_nearby(i);
+               test_modf(i);
+       }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, nearbyint);
+
+       return (atf_no_error());
+}
diff --git a/regress/lib/libm/msun/next_test.c b/regress/lib/libm/msun/next_test.c
new file mode 100644 (file)
index 0000000..650eaf4
--- /dev/null
@@ -0,0 +1,292 @@
+/*     $OpenBSD: next_test.c,v 1.1 2021/10/22 18:00:23 mbuhl Exp $     */
+/*-
+ * Copyright (c) 2005 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Test the correctness of nextafter{,f,l} and nexttoward{,f,l}.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __i386__
+#include <ieeefp.h>
+#endif
+
+#include "test-utils.h"
+
+#define        test(exp, ans, ex)      do {                    \
+       double __ans = (ans);                           \
+       feclearexcept(ALL_STD_EXCEPT);                  \
+       _testl(#exp, __LINE__, (exp), __ans, (ex));     \
+} while (0)
+#define        testf(exp, ans, ex)     do {                    \
+       float __ans = (ans);                            \
+       feclearexcept(ALL_STD_EXCEPT);                  \
+       _testl(#exp, __LINE__, (exp), __ans, (ex));     \
+} while (0)
+#define        testl(exp, ans, ex)     do {                    \
+       long double __ans = (ans);                      \
+       feclearexcept(ALL_STD_EXCEPT);                  \
+       _testl(#exp, __LINE__, (exp), __ans, (ex));     \
+} while (0)
+#define        testboth(arg1, arg2, ans, ex, prec)     do {                    \
+       test##prec(nextafter##prec((arg1), (arg2)), (ans), (ex));       \
+       test##prec(nexttoward##prec((arg1), (arg2)), (ans), (ex));      \
+} while (0)
+#define        testall(arg1, arg2, ans, ex)    do {            \
+       testboth((arg1), (arg2), (ans), (ex), );        \
+       testboth((arg1), (arg2), (ans), (ex), f);       \
+       testboth((arg1), (arg2), (ans), (ex), l);       \
+} while (0)
+
+static void _testl(const char *, int, long double, long double, int);
+static double idd(double);
+static float idf(float);
+
+static const int ex_under = FE_UNDERFLOW | FE_INEXACT; /* shorthand */
+static const int ex_over = FE_OVERFLOW | FE_INEXACT;
+static const long double ldbl_eps = LDBL_EPSILON;
+
+
+
+ATF_TC_WITHOUT_HEAD(zeros);
+ATF_TC_BODY(zeros, tc)
+{
+       long double ldbl_small;
+
+#ifndef        __OpenBSD__
+#ifdef __i386__
+       fpsetprec(FP_PE);
+#endif
+#endif
+       /*
+        * We can't use a compile-time constant here because gcc on
+        * FreeBSD/i386 assumes long doubles are truncated to the
+        * double format.
+        */
+       ldbl_small = ldexpl(1.0, LDBL_MIN_EXP - LDBL_MANT_DIG);
+
+       /*
+        * Special cases involving zeroes.
+        */
+#define        ztest(prec)                                                           \
+       test##prec(copysign##prec(1.0, nextafter##prec(0.0, -0.0)), -1.0, 0); \
+       test##prec(copysign##prec(1.0, nextafter##prec(-0.0, 0.0)), 1.0, 0);  \
+       test##prec(copysign##prec(1.0, nexttoward##prec(0.0, -0.0)), -1.0, 0);\
+       test##prec(copysign##prec(1.0, nexttoward##prec(-0.0, 0.0)), 1.0, 0)
+
+       ztest();
+       ztest(f);
+       ztest(l);
+#undef ztest
+
+#define        stest(next, eps, prec)                                  \
+       test##prec(next(-0.0, 42.0), eps, ex_under);            \
+       test##prec(next(0.0, -42.0), -eps, ex_under);           \
+       test##prec(next(0.0, INFINITY), eps, ex_under);         \
+       test##prec(next(-0.0, -INFINITY), -eps, ex_under)
+
+       stest(nextafter, 0x1p-1074, );
+       stest(nextafterf, 0x1p-149f, f);
+       stest(nextafterl, ldbl_small, l);
+       stest(nexttoward, 0x1p-1074, );
+       stest(nexttowardf, 0x1p-149f, f);
+       stest(nexttowardl, ldbl_small, l);
+#undef stest
+}
+
+ATF_TC_WITHOUT_HEAD(eq_and_nan);
+ATF_TC_BODY(eq_and_nan, tc)
+{
+       /*
+        * `x == y' and NaN tests
+        */
+       testall(42.0, 42.0, 42.0, 0);
+       testall(-42.0, -42.0, -42.0, 0);
+       testall(INFINITY, INFINITY, INFINITY, 0);
+       testall(-INFINITY, -INFINITY, -INFINITY, 0);
+       testall(NAN, 42.0, NAN, 0);
+       testall(42.0, NAN, NAN, 0);
+       testall(NAN, NAN, NAN, 0);
+}
+
+ATF_TC_WITHOUT_HEAD(ordinary);
+ATF_TC_BODY(ordinary, tc)
+{
+       /*
+        * Tests where x is an ordinary normalized number
+        */
+       testboth(1.0, 2.0, 1.0 + DBL_EPSILON, 0, );
+       testboth(1.0, -INFINITY, 1.0 - DBL_EPSILON / 2, 0, );
+       testboth(1.0, 2.0, 1.0 + FLT_EPSILON, 0, f);
+       testboth(1.0, -INFINITY, 1.0 - FLT_EPSILON / 2, 0, f);
+       testboth(1.0, 2.0, 1.0 + ldbl_eps, 0, l);
+       testboth(1.0, -INFINITY, 1.0 - ldbl_eps / 2, 0, l);
+
+       testboth(-1.0, 2.0, -1.0 + DBL_EPSILON / 2, 0, );
+       testboth(-1.0, -INFINITY, -1.0 - DBL_EPSILON, 0, );
+       testboth(-1.0, 2.0, -1.0 + FLT_EPSILON / 2, 0, f);
+       testboth(-1.0, -INFINITY, -1.0 - FLT_EPSILON, 0, f);
+       testboth(-1.0, 2.0, -1.0 + ldbl_eps / 2, 0, l);
+       testboth(-1.0, -INFINITY, -1.0 - ldbl_eps, 0, l);
+
+       /* Cases where nextafter(...) != nexttoward(...) */
+       test(nexttoward(1.0, 1.0 + ldbl_eps), 1.0 + DBL_EPSILON, 0);
+       testf(nexttowardf(1.0, 1.0 + ldbl_eps), 1.0 + FLT_EPSILON, 0);
+       testl(nexttowardl(1.0, 1.0 + ldbl_eps), 1.0 + ldbl_eps, 0);
+}
+
+ATF_TC_WITHOUT_HEAD(boundaries);
+ATF_TC_BODY(boundaries, tc)
+{
+       /*
+        * Tests at word boundaries, normalization boundaries, etc.
+        */
+       testboth(0x1.87654ffffffffp+0, INFINITY, 0x1.87655p+0, 0, );
+       testboth(0x1.87655p+0, -INFINITY, 0x1.87654ffffffffp+0, 0, );
+       testboth(0x1.fffffffffffffp+0, INFINITY, 0x1p1, 0, );
+       testboth(0x1p1, -INFINITY, 0x1.fffffffffffffp+0, 0, );
+       testboth(0x0.fffffffffffffp-1022, INFINITY, 0x1p-1022, 0, );
+       testboth(0x1p-1022, -INFINITY, 0x0.fffffffffffffp-1022, ex_under, );
+
+       testboth(0x1.fffffep0f, INFINITY, 0x1p1, 0, f);
+       testboth(0x1p1, -INFINITY, 0x1.fffffep0f, 0, f);
+       testboth(0x0.fffffep-126f, INFINITY, 0x1p-126f, 0, f);
+       testboth(0x1p-126f, -INFINITY, 0x0.fffffep-126f, ex_under, f);
+
+#if LDBL_MANT_DIG == 53
+       testboth(0x1.87654ffffffffp+0L, INFINITY, 0x1.87655p+0L, 0, l);
+       testboth(0x1.87655p+0L, -INFINITY, 0x1.87654ffffffffp+0L, 0, l);
+       testboth(0x1.fffffffffffffp+0L, INFINITY, 0x1p1L, 0, l);
+       testboth(0x1p1L, -INFINITY, 0x1.fffffffffffffp+0L, 0, l);
+       testboth(0x0.fffffffffffffp-1022L, INFINITY, 0x1p-1022L, 0, l);
+       testboth(0x1p-1022L, -INFINITY, 0x0.fffffffffffffp-1022L, ex_under, l);
+#elif LDBL_MANT_DIG == 64 && !defined(__i386)
+       testboth(0x1.87654321fffffffep+0L, INFINITY, 0x1.87654322p+0L, 0, l);
+       testboth(0x1.87654322p+0L, -INFINITY, 0x1.87654321fffffffep+0L, 0, l);
+       testboth(0x1.fffffffffffffffep0L, INFINITY, 0x1p1L, 0, l);
+       testboth(0x1p1L, -INFINITY, 0x1.fffffffffffffffep0L, 0, l);
+       testboth(0x0.fffffffffffffffep-16382L, INFINITY, 0x1p-16382L, 0, l);
+       testboth(0x1p-16382L, -INFINITY,
+           0x0.fffffffffffffffep-16382L, ex_under, l);
+#elif LDBL_MANT_DIG == 113
+       testboth(0x1.876543210987ffffffffffffffffp+0L, INFINITY,
+           0x1.876543210988p+0, 0, l);
+       testboth(0x1.876543210988p+0L, -INFINITY,
+           0x1.876543210987ffffffffffffffffp+0L, 0, l);
+       testboth(0x1.ffffffffffffffffffffffffffffp0L, INFINITY, 0x1p1L, 0, l);
+       testboth(0x1p1L, -INFINITY, 0x1.ffffffffffffffffffffffffffffp0L, 0, l);
+       testboth(0x0.ffffffffffffffffffffffffffffp-16382L, INFINITY,
+           0x1p-16382L, 0, l);
+       testboth(0x1p-16382L, -INFINITY,
+           0x0.ffffffffffffffffffffffffffffp-16382L, ex_under, l);
+#endif
+}
+
+ATF_TC_WITHOUT_HEAD(overflow);
+ATF_TC_BODY(overflow, tc)
+{
+       long double ldbl_max;
+       /*
+        * We can't use a compile-time constant here because gcc on
+        * FreeBSD/i386 assumes long doubles are truncated to the
+        * double format.
+        */
+       ldbl_max = ldexpl(1.0 - ldbl_eps / 2, LDBL_MAX_EXP);
+
+       /*
+        * Overflow tests
+        */
+       test(idd(nextafter(DBL_MAX, INFINITY)), INFINITY, ex_over);
+       test(idd(nextafter(INFINITY, 0.0)), DBL_MAX, 0);
+       test(idd(nexttoward(DBL_MAX, DBL_MAX * 2.0L)), INFINITY, ex_over);
+#if LDBL_MANT_DIG > 53
+       test(idd(nexttoward(INFINITY, DBL_MAX * 2.0L)), DBL_MAX, 0);
+#endif
+
+       testf(idf(nextafterf(FLT_MAX, INFINITY)), INFINITY, ex_over);
+       testf(idf(nextafterf(INFINITY, 0.0)), FLT_MAX, 0);
+       testf(idf(nexttowardf(FLT_MAX, FLT_MAX * 2.0)), INFINITY, ex_over);
+       testf(idf(nexttowardf(INFINITY, FLT_MAX * 2.0)), FLT_MAX, 0);
+
+       testboth(ldbl_max, INFINITY, INFINITY, ex_over, l);
+       testboth(INFINITY, 0.0, ldbl_max, 0, l);
+}
+
+static void
+_testl(const char *exp, int line, long double actual, long double expected,
+    int except)
+{
+       int actual_except;
+
+       actual_except = fetestexcept(ALL_STD_EXCEPT);
+       if (!fpequal_cs(actual, expected, true)) {
+               atf_tc_fail_check(__FILE__, line,
+                   "%s returned %La, expecting %La\n", exp, actual, expected);
+       }
+       if (actual_except != except) {
+               atf_tc_fail_check(__FILE__, line,
+                   "%s raised 0x%x, expecting 0x%x\n", exp, actual_except,
+                   except);
+       }
+}
+
+/*
+ * The idd() and idf() routines ensure that doubles and floats are
+ * converted to their respective types instead of stored in the FPU
+ * with extra precision.
+ */
+static double
+idd(double x)
+{
+       return (x);
+}
+
+static float
+idf(float x)
+{
+       return (x);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, zeros);
+       ATF_TP_ADD_TC(tp, ordinary);
+       ATF_TP_ADD_TC(tp, eq_and_nan);
+       ATF_TP_ADD_TC(tp, boundaries);
+       ATF_TP_ADD_TC(tp, overflow);
+
+       return (atf_no_error());
+}
diff --git a/regress/lib/libm/msun/rem_test.c b/regress/lib/libm/msun/rem_test.c
new file mode 100644 (file)
index 0000000..6a50ca2
--- /dev/null
@@ -0,0 +1,217 @@
+/*     $OpenBSD: rem_test.c,v 1.1 2021/10/22 18:00:23 mbuhl Exp $      */
+/*-
+ * Copyright (c) 2005-2008 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Test for remainder functions: remainder, remainderf, remainderl,
+ * remquo, remquof, and remquol.
+ * Missing tests: fmod, fmodf.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include "test-utils.h"
+
+static void test_invalid(long double, long double);
+static void testl(long double, long double, long double, int);
+static void testd(double, double, double, int);
+static void testf(float, float, float, int);
+
+#define        test(x, y, e_r, e_q) do {       \
+       testl(x, y, e_r, e_q);          \
+       testd(x, y, e_r, e_q);          \
+       testf(x, y, e_r, e_q);          \
+} while (0)
+
+ATF_TC_WITHOUT_HEAD(rem1);
+ATF_TC_BODY(rem1, tc)
+{
+       test_invalid(0.0, 0.0);
+       test_invalid(1.0, 0.0);
+       test_invalid(INFINITY, 0.0);
+       test_invalid(INFINITY, 1.0);
+       test_invalid(-INFINITY, 1.0);
+       test_invalid(NAN, 1.0);
+       test_invalid(1.0, NAN);
+
+       test(4, 4, 0, 1);
+       test(0, 3.0, 0, 0);
+       testd(0x1p-1074, 1, 0x1p-1074, 0);
+       testf(0x1p-149, 1, 0x1p-149, 0);
+       test(3.0, 4, -1, 1);
+       test(3.0, -4, -1, -1);
+       testd(275 * 1193040, 275, 0, 1193040);
+       test(4.5 * 7.5, 4.5, -2.25, 8); /* we should get the even one */
+       testf(0x1.9044f6p-1, 0x1.ce662ep-1, -0x1.f109cp-4, 1);
+#if LDBL_MANT_DIG > 53
+       testl(-0x1.23456789abcdefp-2000L, 0x1.fedcba987654321p-2000L,
+           0x1.b72ea61d950c862p-2001L, -1);
+#endif
+}
+
+ATF_TC_WITHOUT_HEAD(rem2);
+ATF_TC_BODY(rem2, tc)
+{
+       /*
+        * The actual quotient here is 864062210.50000003..., but
+        * double-precision division gets -8.64062210.5, which rounds
+        * the wrong way.  This test ensures that remquo() is smart
+        * enough to get the low-order bit right.
+        */
+       testd(-0x1.98260f22fc6dep-302, 0x1.fb3167c430a13p-332,
+           0x1.fb3165b82de72p-333, -864062211);
+       /* Even harder cases with greater exponent separation */
+       test(0x1.fp100, 0x1.ep-40, -0x1.cp-41, 143165577);
+       testd(-0x1.abcdefp120, 0x1.87654321p-120, -0x1.69c78ec4p-121,
+           -63816414);
+}
+
+ATF_TC_WITHOUT_HEAD(rem3);
+ATF_TC_BODY(rem3, tc)
+{
+       test(0x1.66666cp+120, 0x1p+71, 0.0, 1476395008);
+       testd(-0x1.0000000000003p+0, 0x1.0000000000003p+0, -0.0, -1);
+       testl(-0x1.0000000000003p+0, 0x1.0000000000003p+0, -0.0, -1);
+       testd(-0x1.0000000000001p-749, 0x1.4p-1072, 0x1p-1074, -1288490189);
+       testl(-0x1.0000000000001p-749, 0x1.4p-1072, 0x1p-1074, -1288490189);
+}
+
+static void
+test_invalid(long double x, long double y)
+{
+       int q;
+
+       q = 0xdeadbeef;
+
+       ATF_CHECK(isnan(remainder(x, y)));
+       ATF_CHECK(isnan(remquo(x, y, &q)));
+#ifdef STRICT
+       ATF_CHECK(q == 0xdeadbeef);
+#endif
+
+       ATF_CHECK(isnan(remainderf(x, y)));
+       ATF_CHECK(isnan(remquof(x, y, &q)));
+#ifdef STRICT
+       ATF_CHECK(q == 0xdeadbeef);
+#endif
+
+       ATF_CHECK(isnan(remainderl(x, y)));
+       ATF_CHECK(isnan(remquol(x, y, &q)));
+#ifdef STRICT
+       ATF_CHECK(q == 0xdeadbeef);
+#endif
+}
+
+/* 0x012345 ==> 0x01ffff */
+static inline int
+mask(int x)
+{
+       return ((unsigned)~0 >> (32 - fls(x)));
+}
+
+static void
+testl(long double x, long double y, long double expected_rem, int expected_quo)
+{
+       int q;
+       long double rem;
+
+       q = random();
+       rem = remainderl(x, y);
+       ATF_CHECK(rem == expected_rem);
+       ATF_CHECK(!signbit(rem) == !signbit(expected_rem));
+       rem = remquol(x, y, &q);
+       ATF_CHECK(rem == expected_rem);
+       ATF_CHECK(!signbit(rem) == !signbit(expected_rem));
+       ATF_CHECK((q ^ expected_quo) >= 0); /* sign(q) == sign(expected_quo) */
+       ATF_CHECK((q & 0x7) == (expected_quo & 0x7));
+       if (q != 0) {
+               ATF_CHECK((q > 0) ^ !(expected_quo > 0));
+               q = abs(q);
+               ATF_CHECK(q == (abs(expected_quo) & mask(q)));
+       }
+}
+
+static void
+testd(double x, double y, double expected_rem, int expected_quo)
+{
+       int q;
+       double rem;
+
+       q = random();
+       rem = remainder(x, y);
+       ATF_CHECK(rem == expected_rem);
+       ATF_CHECK(!signbit(rem) == !signbit(expected_rem));
+       rem = remquo(x, y, &q);
+       ATF_CHECK(rem == expected_rem);
+       ATF_CHECK(!signbit(rem) == !signbit(expected_rem));
+       ATF_CHECK((q ^ expected_quo) >= 0); /* sign(q) == sign(expected_quo) */
+       ATF_CHECK((q & 0x7) == (expected_quo & 0x7));
+       if (q != 0) {
+               ATF_CHECK((q > 0) ^ !(expected_quo > 0));
+               q = abs(q);
+               ATF_CHECK(q == (abs(expected_quo) & mask(q)));
+       }
+}
+
+static void
+testf(float x, float y, float expected_rem, int expected_quo)
+{
+       int q;
+       float rem;
+
+       q = random();
+       rem = remainderf(x, y);
+       ATF_CHECK(rem == expected_rem);
+       ATF_CHECK(!signbit(rem) == !signbit(expected_rem));
+       rem = remquof(x, y, &q);
+       ATF_CHECK(rem == expected_rem);
+       ATF_CHECK(!signbit(rem) == !signbit(expected_rem));
+       ATF_CHECK((q ^ expected_quo) >= 0); /* sign(q) == sign(expected_quo) */
+       ATF_CHECK((q & 0x7) == (expected_quo & 0x7));
+       if (q != 0) {
+               ATF_CHECK((q > 0) ^ !(expected_quo > 0));
+               q = abs(q);
+               ATF_CHECK((q & mask(q)) == (abs(expected_quo) & mask(q)));
+       }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, rem1);
+       ATF_TP_ADD_TC(tp, rem2);
+       ATF_TP_ADD_TC(tp, rem3);
+
+       return (atf_no_error());
+}
index 08bf715..2217531 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: test-utils.h,v 1.2 2019/02/21 17:36:41 bluhm Exp $    */
+/*     $OpenBSD: test-utils.h,v 1.3 2021/10/22 18:00:23 mbuhl Exp $    */
 /*-
  * Copyright (c) 2005-2013 David Schultz <das@FreeBSD.org>
  * All rights reserved.
@@ -32,6 +32,9 @@
 
 #include <complex.h>
 #include <fenv.h>
+#include <float.h>
+
+#include "atf-c.h"
 
 /*
  * Implementations are permitted to define additional exception flags
@@ -89,34 +92,21 @@ CMPLXL(long double x, long double y)
 }
 #endif
 
-static int     fpequal(long double, long double) __used;
-static int     cfpequal(long double complex, long double complex) __used;
-static int     cfpequal_cs(long double complex, long double complex,
-                   int) __used;
-static int     cfpequal_tol(long double complex, long double complex,
-                   long double, unsigned int) __used;
-
 /*
- * Compare d1 and d2 using special rules: NaN == NaN and +0 != -0.
- * Fail an assertion if they differ.
+ * The compiler-rt fp128 builtins do not update FP exceptions.
+ * See https://llvm.org/PR34126
  */
-static int
-fpequal(long double d1, long double d2)
-{
 
-       if (d1 != d2)
-               return (isnan(d1) && isnan(d2));
-       return (copysignl(1.0, d1) == copysignl(1.0, d2));
-}
+static int     cfpequal(long double complex, long double complex) __used;
 
 /*
  * Determine whether x and y are equal, with two special rules:
  *     +0.0 != -0.0
  *      NaN == NaN
- * If checksign is 0, we compare the absolute values instead.
+ * If checksign is false, we compare the absolute values instead.
  */
-static int
-fpequal_cs(long double x, long double y, int checksign)
+static inline int
+fpequal_cs(long double x, long double y, bool checksign)
 {
        if (isnan(x) && isnan(y))
                return (1);
@@ -126,7 +116,7 @@ fpequal_cs(long double x, long double y, int checksign)
                return (fabsl(x) == fabsl(y));
 }
 
-static int
+static inline int
 fpequal_tol(long double x, long double y, long double tol,
     unsigned int flags)
 {
@@ -157,27 +147,70 @@ fpequal_tol(long double x, long double y, long double tol,
        return (ret);
 }
 
-static int
+#define CHECK_FPEQUAL(x, y) CHECK_FPEQUAL_CS(x, y, true)
+
+#define CHECK_FPEQUAL_CS(x, y, checksign) do {                                 \
+       long double _x = x;                                                     \
+       long double _y = y;                                                     \
+       ATF_CHECK_MSG(fpequal_cs(_x, _y, checksign),                            \
+           "%s (%.25Lg) ~= %s (%.25Lg)", #x, _x, #y, _y);                      \
+} while (0)
+
+#define CHECK_FPEQUAL_TOL(x, y, tol, flags) do {                               \
+       long double _x = x;                                                     \
+       long double _y = y;                                                     \
+       bool eq = fpequal_tol(_x, _y, tol, flags);                              \
+       long double _diff = eq ? 0.0L : fabsl(_x - _y);                         \
+       ATF_CHECK_MSG(eq, "%s (%.25Lg) ~= %s (%.25Lg), diff=%Lg, maxdiff=%Lg,", \
+           #x, _x, #y, _y, _diff, fabsl(_y * tol));                            \
+} while (0)
+
+static inline int
 cfpequal(long double complex d1, long double complex d2)
 {
 
-       return (fpequal(creall(d1), creall(d2)) &&
-           fpequal(cimagl(d1), cimagl(d2)));
+       return (fpequal_cs(creall(d1), creall(d2), true) &&
+           fpequal_cs(cimagl(d1), cimagl(d2), true));
 }
 
+#ifdef __OpenBSD__
 static int
-cfpequal_cs(long double complex x, long double complex y, int checksign)
+cfpequal_cs(x, y, checksign)
 {
-       return (fpequal_cs(creal(x), creal(y), checksign)
-               && fpequal_cs(cimag(x), cimag(y), checksign));
+       long double _x = x;
+       long double _y = y;
+       return
+           fpequal_cs(creal(_x), creal(_y), (checksign & CS_REAL) != 0) &&
+           fpequal_cs(cimag(_x), cimag(_y), (checksign & CS_IMAG) != 0);
 }
+#endif
 
-static int
-cfpequal_tol(long double complex x, long double complex y, long double tol,
-    unsigned int flags)
-{
-       return (fpequal_tol(creal(x), creal(y), tol, flags)
-               && fpequal_tol(cimag(x), cimag(y), tol, flags));
-}
+#define CHECK_CFPEQUAL_CS(x, y, checksign) do {                                        \
+       long double _x = x;                                                     \
+       long double _y = y;                                                     \
+       bool equal_cs =                                                         \
+           fpequal_cs(creal(_x), creal(_y), (checksign & CS_REAL) != 0) &&     \
+           fpequal_cs(cimag(_x), cimag(_y), (checksign & CS_IMAG) != 0);       \
+       ATF_CHECK_MSG(equal_cs, "%s (%Lg + %Lg I) ~=  %s (%Lg + %Lg I)",        \
+           #x, creall(_x), cimagl(_x), #y, creall(_y), cimagl(_y));            \
+} while (0)
+
+#define CHECK_CFPEQUAL_TOL(x, y, tol, flags) do {                              \
+       long double _x = x;                                                     \
+       long double _y = y;                                                     \
+       bool equal_tol = (fpequal_tol(creal(_x), creal(_y), tol, flags) &&      \
+           fpequal_tol(cimag(_x), cimag(_y), tol, flags));                     \
+       ATF_CHECK_MSG(equal_tol, "%s (%Lg + %Lg I) ~=  %s (%Lg + %Lg I)",       \
+           #x, creall(_x), cimagl(_x), #y, creall(_y), cimagl(_y));            \
+} while (0)
+
+#define CHECK_FP_EXCEPTIONS(excepts, exceptmask)               \
+       ATF_CHECK_EQ_MSG((excepts), fetestexcept(exceptmask),   \
+           "unexpected exception flags: got %#x not %#x",      \
+           fetestexcept(exceptmask), (excepts))
+#define CHECK_FP_EXCEPTIONS_MSG(excepts, exceptmask, fmt, ...) \
+       ATF_CHECK_EQ_MSG((excepts), fetestexcept(exceptmask),   \
+           "unexpected exception flags: got %#x not %#x " fmt, \
+           fetestexcept(exceptmask), (excepts), __VA_ARGS__)
 
 #endif /* _TEST_UTILS_H_ */
diff --git a/regress/lib/libm/msun/trig_test.c b/regress/lib/libm/msun/trig_test.c
new file mode 100644 (file)
index 0000000..040690f
--- /dev/null
@@ -0,0 +1,286 @@
+/*     $OpenBSD: trig_test.c,v 1.1 2021/10/22 18:00:23 mbuhl Exp $     */
+/*-
+ * Copyright (c) 2008 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+/*
+ * Tests for corner cases in trigonometric functions. Some accuracy tests
+ * are included as well, but these are very basic sanity checks, not
+ * intended to be comprehensive.
+ *
+ * The program for generating representable numbers near multiples of pi is
+ * available at http://www.cs.berkeley.edu/~wkahan/testpi/ .
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "test-utils.h"
+
+#pragma STDC FENV_ACCESS ON
+
+/*
+ * Test that a function returns the correct value and sets the
+ * exception flags correctly. The exceptmask specifies which
+ * exceptions we should check. We need to be lenient for several
+ * reasons, but mainly because on some architectures it's impossible
+ * to raise FE_OVERFLOW without raising FE_INEXACT.
+ *
+ * These are macros instead of functions so that assert provides more
+ * meaningful error messages.
+ *
+ * XXX The volatile here is to avoid gcc's bogus constant folding and work
+ *     around the lack of support for the FENV_ACCESS pragma.
+ */
+#define        test(func, x, result, exceptmask, excepts)      do {            \
+       volatile long double _d = x;                                    \
+       ATF_CHECK(feclearexcept(FE_ALL_EXCEPT) == 0);                   \
+       CHECK_FPEQUAL((func)(_d), (result));                    \
+       CHECK_FP_EXCEPTIONS_MSG(excepts, exceptmask, "for %s(%s)",      \
+           #func, #x);                                                 \
+} while (0)
+
+#define        testall(prefix, x, result, exceptmask, excepts) do {            \
+       test(prefix, x, (double)result, exceptmask, excepts);           \
+       test(prefix##f, x, (float)result, exceptmask, excepts);         \
+       test(prefix##l, x, result, exceptmask, excepts);                \
+} while (0)
+
+#define        testdf(prefix, x, result, exceptmask, excepts)  do {            \
+       test(prefix, x, (double)result, exceptmask, excepts);           \
+       test(prefix##f, x, (float)result, exceptmask, excepts);         \
+} while (0)
+
+ATF_TC(special);
+ATF_TC_HEAD(special, tc)
+{
+
+       atf_tc_set_md_var(tc, "descr",
+           "test special cases in sin(), cos(), and tan()");
+}
+ATF_TC_BODY(special, tc)
+{
+
+       /* Values at 0 should be exact. */
+       testall(tan, 0.0, 0.0, ALL_STD_EXCEPT, 0);
+       testall(tan, -0.0, -0.0, ALL_STD_EXCEPT, 0);
+       testall(cos, 0.0, 1.0, ALL_STD_EXCEPT, 0);
+       testall(cos, -0.0, 1.0, ALL_STD_EXCEPT, 0);
+       testall(sin, 0.0, 0.0, ALL_STD_EXCEPT, 0);
+       testall(sin, -0.0, -0.0, ALL_STD_EXCEPT, 0);
+
+       /* func(+-Inf) == NaN */
+       testall(tan, INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID);
+       testall(sin, INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID);
+       testall(cos, INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID);
+       testall(tan, -INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID);
+       testall(sin, -INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID);
+       testall(cos, -INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID);
+
+       /* func(NaN) == NaN */
+       testall(tan, NAN, NAN, ALL_STD_EXCEPT, 0);
+       testall(sin, NAN, NAN, ALL_STD_EXCEPT, 0);
+       testall(cos, NAN, NAN, ALL_STD_EXCEPT, 0);
+}
+
+#ifndef __i386__
+ATF_TC(reduction);
+ATF_TC_HEAD(reduction, tc)
+{
+
+       atf_tc_set_md_var(tc, "descr",
+           "tests to ensure argument reduction for large arguments is accurate");
+}
+ATF_TC_BODY(reduction, tc)
+{
+       /* floats very close to odd multiples of pi */
+       static const float f_pi_odd[] = {
+               85563208.0f,
+               43998769152.0f,
+               9.2763667655669323e+25f,
+               1.5458357838905804e+29f,
+       };
+       /* doubles very close to odd multiples of pi */
+       static const double d_pi_odd[] = {
+               3.1415926535897931,
+               91.106186954104004,
+               642615.9188844458,
+               3397346.5699258847,
+               6134899525417045.0,
+               3.0213551960457761e+43,
+               1.2646209897993783e+295,
+               6.2083625380677099e+307,
+       };
+       /* long doubles very close to odd multiples of pi */
+#if LDBL_MANT_DIG == 64
+       static const long double ld_pi_odd[] = {
+               1.1891886960373841596e+101L,
+               1.07999475322710967206e+2087L,
+               6.522151627890431836e+2147L,
+               8.9368974898260328229e+2484L,
+               9.2961044110572205863e+2555L,
+               4.90208421886578286e+3189L,
+               1.5275546401232615884e+3317L,
+               1.7227465626338900093e+3565L,
+               2.4160090594000745334e+3808L,
+               9.8477555741888350649e+4314L,
+               1.6061597222105160737e+4326L,
+       };
+#endif
+
+       unsigned i;
+
+#if defined(__amd64__) && defined(__clang__) && __clang_major__ >= 7 && \
+    __clang_major__ < 10 && __FreeBSD_cc_version < 1300002
+       atf_tc_expect_fail("test fails with clang 7-9 - bug 234040");
+#endif
+
+       for (i = 0; i < nitems(f_pi_odd); i++) {
+               ATF_CHECK(fabs(sinf(f_pi_odd[i])) < FLT_EPSILON);
+               ATF_CHECK(cosf(f_pi_odd[i]) == -1.0);
+               ATF_CHECK(fabs(tan(f_pi_odd[i])) < FLT_EPSILON);
+
+               ATF_CHECK(fabs(sinf(-f_pi_odd[i])) < FLT_EPSILON);
+               ATF_CHECK(cosf(-f_pi_odd[i]) == -1.0);
+               ATF_CHECK(fabs(tanf(-f_pi_odd[i])) < FLT_EPSILON);
+
+               ATF_CHECK(fabs(sinf(f_pi_odd[i] * 2)) < FLT_EPSILON);
+               ATF_CHECK(cosf(f_pi_odd[i] * 2) == 1.0);
+               ATF_CHECK(fabs(tanf(f_pi_odd[i] * 2)) < FLT_EPSILON);
+
+               ATF_CHECK(fabs(sinf(-f_pi_odd[i] * 2)) < FLT_EPSILON);
+               ATF_CHECK(cosf(-f_pi_odd[i] * 2) == 1.0);
+               ATF_CHECK(fabs(tanf(-f_pi_odd[i] * 2)) < FLT_EPSILON);
+       }
+
+       for (i = 0; i < nitems(d_pi_odd); i++) {
+               ATF_CHECK(fabs(sin(d_pi_odd[i])) < 2 * DBL_EPSILON);
+               ATF_CHECK(cos(d_pi_odd[i]) == -1.0);
+               ATF_CHECK(fabs(tan(d_pi_odd[i])) < 2 * DBL_EPSILON);
+
+               ATF_CHECK(fabs(sin(-d_pi_odd[i])) < 2 * DBL_EPSILON);
+               ATF_CHECK(cos(-d_pi_odd[i]) == -1.0);
+               ATF_CHECK(fabs(tan(-d_pi_odd[i])) < 2 * DBL_EPSILON);
+
+               ATF_CHECK(fabs(sin(d_pi_odd[i] * 2)) < 2 * DBL_EPSILON);
+               ATF_CHECK(cos(d_pi_odd[i] * 2) == 1.0);
+               ATF_CHECK(fabs(tan(d_pi_odd[i] * 2)) < 2 * DBL_EPSILON);
+
+               ATF_CHECK(fabs(sin(-d_pi_odd[i] * 2)) < 2 * DBL_EPSILON);
+               ATF_CHECK(cos(-d_pi_odd[i] * 2) == 1.0);
+               ATF_CHECK(fabs(tan(-d_pi_odd[i] * 2)) < 2 * DBL_EPSILON);
+       }
+
+#if LDBL_MANT_DIG == 64 /* XXX: || LDBL_MANT_DIG == 113 */
+       for (i = 0; i < nitems(ld_pi_odd); i++) {
+               ATF_CHECK(fabsl(sinl(ld_pi_odd[i])) < LDBL_EPSILON);
+               ATF_CHECK(cosl(ld_pi_odd[i]) == -1.0);
+               ATF_CHECK(fabsl(tanl(ld_pi_odd[i])) < LDBL_EPSILON);
+
+               ATF_CHECK(fabsl(sinl(-ld_pi_odd[i])) < LDBL_EPSILON);
+               ATF_CHECK(cosl(-ld_pi_odd[i]) == -1.0);
+               ATF_CHECK(fabsl(tanl(-ld_pi_odd[i])) < LDBL_EPSILON);
+
+               ATF_CHECK(fabsl(sinl(ld_pi_odd[i] * 2)) < LDBL_EPSILON);
+               ATF_CHECK(cosl(ld_pi_odd[i] * 2) == 1.0);
+               ATF_CHECK(fabsl(tanl(ld_pi_odd[i] * 2)) < LDBL_EPSILON);
+
+               ATF_CHECK(fabsl(sinl(-ld_pi_odd[i] * 2)) < LDBL_EPSILON);
+               ATF_CHECK(cosl(-ld_pi_odd[i] * 2) == 1.0);
+               ATF_CHECK(fabsl(tanl(-ld_pi_odd[i] * 2)) < LDBL_EPSILON);
+       }
+#endif
+}
+
+ATF_TC(accuracy);
+ATF_TC_HEAD(accuracy, tc)
+{
+
+       atf_tc_set_md_var(tc, "descr",
+           "tests the accuracy of these functions over the primary range");
+}
+ATF_TC_BODY(accuracy, tc)
+{
+
+       /* For small args, sin(x) = tan(x) = x, and cos(x) = 1. */
+       testall(sin, 0xd.50ee515fe4aea16p-114L, 0xd.50ee515fe4aea16p-114L,
+            ALL_STD_EXCEPT, FE_INEXACT);
+       testall(tan, 0xd.50ee515fe4aea16p-114L, 0xd.50ee515fe4aea16p-114L,
+            ALL_STD_EXCEPT, FE_INEXACT);
+       testall(cos, 0xd.50ee515fe4aea16p-114L, 1.0,
+               ALL_STD_EXCEPT, FE_INEXACT);
+
+       /*
+        * These tests should pass for f32, d64, and ld80 as long as
+        * the error is <= 0.75 ulp (round to nearest)
+        */
+#if LDBL_MANT_DIG <= 64
+#define        testacc testall
+#else
+#define        testacc testdf
+#endif
+       testacc(sin, 0.17255452780841205174L, 0.17169949801444412683L,
+               ALL_STD_EXCEPT, FE_INEXACT);
+       testacc(sin, -0.75431944555904520893L, -0.68479288156557286353L,
+               ALL_STD_EXCEPT, FE_INEXACT);
+       testacc(cos, 0.70556358769838947292L, 0.76124620693117771850L,
+               ALL_STD_EXCEPT, FE_INEXACT);
+       testacc(cos, -0.34061437849088045332L, 0.94254960031831729956L,
+               ALL_STD_EXCEPT, FE_INEXACT);
+       testacc(tan, -0.15862817413325692897L, -0.15997221861309522115L,
+               ALL_STD_EXCEPT, FE_INEXACT);
+       testacc(tan, 0.38374784931303813530L, 0.40376500259976759951L,
+               ALL_STD_EXCEPT, FE_INEXACT);
+
+       /*
+        * XXX missing:
+        * - tests for ld128
+        * - tests for other rounding modes (probably won't pass for now)
+        * - tests for large numbers that get reduced to hi+lo with lo!=0
+        */
+}
+#endif
+
+ATF_TP_ADD_TCS(tp)
+{
+
+       ATF_TP_ADD_TC(tp, special);
+
+#ifndef __i386__
+       ATF_TP_ADD_TC(tp, accuracy);
+       ATF_TP_ADD_TC(tp, reduction);
+#endif
+
+       return (atf_no_error());
+}