From: mbuhl Date: Fri, 22 Oct 2021 18:00:22 +0000 (+0000) Subject: add missing FreeBSD libm tests, update the others X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=c36e572ec6e4c7a6aca6564610c2c45de467ead4;p=openbsd add missing FreeBSD libm tests, update the others --- diff --git a/regress/lib/libm/msun/Makefile b/regress/lib/libm/msun/Makefile index 16f916913db..1894ef6a352 100644 --- a/regress/lib/libm/msun/Makefile +++ b/regress/lib/libm/msun/Makefile @@ -1,14 +1,111 @@ -# $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 +# +# 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 diff --git a/regress/lib/libm/msun/atf-c.c b/regress/lib/libm/msun/atf-c.c new file mode 100644 index 00000000000..f8774da9986 --- /dev/null +++ b/regress/lib/libm/msun/atf-c.c @@ -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 + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000000..11bf1d85d8a --- /dev/null +++ b/regress/lib/libm/msun/atf-c.h @@ -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 + * + * 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 +#include +#include +#include +#include + +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 index 00000000000..f94362952a3 --- /dev/null +++ b/regress/lib/libm/msun/cexp_test.c @@ -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 + * 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 +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include +#include + +#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()); +} diff --git a/regress/lib/libm/msun/conj_test.c b/regress/lib/libm/msun/conj_test.c index 709b21cfe49..ce2135142ee 100644 --- a/regress/lib/libm/msun/conj_test.c +++ b/regress/lib/libm/msun/conj_test.c @@ -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 * All rights reserved. @@ -32,7 +32,6 @@ #include /* $FreeBSD: head/lib/msun/tests/conj_test.c 314650 2017-03-04 10:07:46Z ngie $ */ -#include #include #include #include @@ -42,24 +41,6 @@ #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 index 00000000000..adbd427d04d --- /dev/null +++ b/regress/lib/libm/msun/csqrt_test.c @@ -0,0 +1,369 @@ +/* $OpenBSD: csqrt_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $ */ +/*- + * Copyright (c) 2007 David Schultz + * 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 +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include + +#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 index 00000000000..d571d647eb7 --- /dev/null +++ b/regress/lib/libm/msun/ctrig_test.c @@ -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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#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 _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 index 00000000000..c1b49ad9fe9 --- /dev/null +++ b/regress/lib/libm/msun/exponential_test.c @@ -0,0 +1,197 @@ +/* $OpenBSD: exponential_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $ */ +/*- + * Copyright (c) 2008 David Schultz + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#ifdef __i386__ +#include +#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()); +} diff --git a/regress/lib/libm/msun/fenv_test.c b/regress/lib/libm/msun/fenv_test.c index 46f7406e84c..529d8cc5e09 100644 --- a/regress/lib/libm/msun/fenv_test.c +++ b/regress/lib/libm/msun/fenv_test.c @@ -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 * All rights reserved. @@ -25,6 +25,8 @@ * SUCH DAMAGE. */ +#include "macros.h" + /* * Test the correctness and C99-compliance of various fenv.h features. */ @@ -38,19 +40,16 @@ #include #include #include +#ifndef __OpenBSD__ +#include +#endif #include #include #include #include #include -/* - * 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 index 00000000000..f588acfc900 --- /dev/null +++ b/regress/lib/libm/msun/fls.c @@ -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 +__FBSDID("$FreeBSD$"); + +#include + +/* + * 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 index 00000000000..ce1dfee6072 --- /dev/null +++ b/regress/lib/libm/msun/fma_test.c @@ -0,0 +1,557 @@ +/* $OpenBSD: fma_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $ */ +/*- + * Copyright (c) 2008 David Schultz + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#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 index 00000000000..e84a94c1c9d --- /dev/null +++ b/regress/lib/libm/msun/fmaxmin_test.c @@ -0,0 +1,188 @@ +/* $OpenBSD: fmaxmin_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $ */ +/*- + * Copyright (c) 2008 David Schultz + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#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()); +} diff --git a/regress/lib/libm/msun/ilogb_test.c b/regress/lib/libm/msun/ilogb_test.c index bd516a86ac5..3d482efbb57 100644 --- a/regress/lib/libm/msun/ilogb_test.c +++ b/regress/lib/libm/msun/ilogb_test.c @@ -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. @@ -26,58 +27,78 @@ * $FreeBSD: head/lib/msun/tests/ilogb_test.c 292328 2015-12-16 09:11:11Z ngie $ */ -#include #include #include #include #include #include -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 index 00000000000..9502ddfa408 --- /dev/null +++ b/regress/lib/libm/msun/invctrig_test.c @@ -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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#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 index 00000000000..7c06a897313 --- /dev/null +++ b/regress/lib/libm/msun/invtrig_test.c @@ -0,0 +1,460 @@ +/* $OpenBSD: invtrig_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $ */ +/*- + * Copyright (c) 2008 David Schultz + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#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 index 00000000000..2263c81cfc0 --- /dev/null +++ b/regress/lib/libm/msun/logarithm_test.c @@ -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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#ifdef __i386__ +#include +#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()); +} diff --git a/regress/lib/libm/msun/lrint_test.c b/regress/lib/libm/msun/lrint_test.c index fa1b1737780..5a1e281abc4 100644 --- a/regress/lib/libm/msun/lrint_test.c +++ b/regress/lib/libm/msun/lrint_test.c @@ -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 * All rights reserved. @@ -32,21 +32,21 @@ #include /* $FreeBSD: head/lib/msun/tests/lrint_test.c 314650 2017-03-04 10:07:46Z ngie $ */ -#include #include #include #include #include -/* - * 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 { \ @@ -65,12 +65,11 @@ 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 index 00000000000..9f83b09f8f1 --- /dev/null +++ b/regress/lib/libm/msun/lround_test.c @@ -0,0 +1,123 @@ +/* $OpenBSD: lround_test.c,v 1.1 2021/10/22 18:00:22 mbuhl Exp $ */ +/*- + * Copyright (c) 2005 David Schultz + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#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 index 00000000000..ed682c29e37 --- /dev/null +++ b/regress/lib/libm/msun/macros.h @@ -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 index 00000000000..833290d8872 --- /dev/null +++ b/regress/lib/libm/msun/nan_test.c @@ -0,0 +1,126 @@ +/* $OpenBSD: nan_test.c,v 1.1 2021/10/22 18:00:23 mbuhl Exp $ */ +/*- + * Copyright (C) 2007 David Schultz + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000000..35ec3724faa --- /dev/null +++ b/regress/lib/libm/msun/nearbyint_test.c @@ -0,0 +1,174 @@ +/* $OpenBSD: nearbyint_test.c,v 1.1 2021/10/22 18:00:23 mbuhl Exp $ */ +/*- + * Copyright (c) 2010 David Schultz + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#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 index 00000000000..650eaf46f23 --- /dev/null +++ b/regress/lib/libm/msun/next_test.c @@ -0,0 +1,292 @@ +/* $OpenBSD: next_test.c,v 1.1 2021/10/22 18:00:23 mbuhl Exp $ */ +/*- + * Copyright (c) 2005 David Schultz + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#ifdef __i386__ +#include +#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 index 00000000000..6a50ca23a42 --- /dev/null +++ b/regress/lib/libm/msun/rem_test.c @@ -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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#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()); +} diff --git a/regress/lib/libm/msun/test-utils.h b/regress/lib/libm/msun/test-utils.h index 08bf71535e3..2217531b183 100644 --- a/regress/lib/libm/msun/test-utils.h +++ b/regress/lib/libm/msun/test-utils.h @@ -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 * All rights reserved. @@ -32,6 +32,9 @@ #include #include +#include + +#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 index 00000000000..040690f5266 --- /dev/null +++ b/regress/lib/libm/msun/trig_test.c @@ -0,0 +1,286 @@ +/* $OpenBSD: trig_test.c,v 1.1 2021/10/22 18:00:23 mbuhl Exp $ */ +/*- + * Copyright (c) 2008 David Schultz + * 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 +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include + +#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()); +}