Changeset View
Changeset View
Standalone View
Standalone View
lib/msun/tests/fma_test.c
Show All 26 Lines | |||||
/* | /* | ||||
* Tests for fma{,f,l}(). | * Tests for fma{,f,l}(). | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <assert.h> | |||||
#include <fenv.h> | #include <fenv.h> | ||||
#include <float.h> | #include <float.h> | ||||
#include <math.h> | #include <math.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include "test-utils.h" | #include "test-utils.h" | ||||
#pragma STDC FENV_ACCESS ON | #pragma STDC FENV_ACCESS ON | ||||
/* | /* | ||||
* Test that a function returns the correct value and sets the | * Test that a function returns the correct value and sets the | ||||
* exception flags correctly. The exceptmask specifies which | * exception flags correctly. The exceptmask specifies which | ||||
* exceptions we should check. We need to be lenient for several | * exceptions we should check. We need to be lenient for several | ||||
* reasons, but mainly because on some architectures it's impossible | * reasons, but mainly because on some architectures it's impossible | ||||
* to raise FE_OVERFLOW without raising FE_INEXACT. | * to raise FE_OVERFLOW without raising FE_INEXACT. | ||||
* | * | ||||
* These are macros instead of functions so that assert provides more | * These are macros instead of functions so that assert provides more | ||||
* meaningful error messages. | * meaningful error messages. | ||||
*/ | */ | ||||
#define test(func, x, y, z, result, exceptmask, excepts) do { \ | #define test(func, x, y, z, result, exceptmask, excepts) do { \ | ||||
volatile long double _vx = (x), _vy = (y), _vz = (z); \ | volatile long double _vx = (x), _vy = (y), _vz = (z); \ | ||||
assert(feclearexcept(FE_ALL_EXCEPT) == 0); \ | ATF_CHECK(feclearexcept(FE_ALL_EXCEPT) == 0); \ | ||||
assert(fpequal((func)(_vx, _vy, _vz), (result))); \ | ATF_CHECK(fpequal((func)(_vx, _vy, _vz), (result))); \ | ||||
assert(((void)(func), fetestexcept(exceptmask) == (excepts))); \ | CHECK_FP_EXCEPTIONS_MSG(excepts, exceptmask, "for %s(%s)", \ | ||||
#func, #x); \ | |||||
} while (0) | } while (0) | ||||
#define testall(x, y, z, result, exceptmask, excepts) do { \ | #define testall(x, y, z, result, exceptmask, excepts) do { \ | ||||
test(fma, (double)(x), (double)(y), (double)(z), \ | test(fma, (double)(x), (double)(y), (double)(z), \ | ||||
(double)(result), (exceptmask), (excepts)); \ | (double)(result), (exceptmask), (excepts)); \ | ||||
test(fmaf, (float)(x), (float)(y), (float)(z), \ | test(fmaf, (float)(x), (float)(y), (float)(z), \ | ||||
(float)(result), (exceptmask), (excepts)); \ | (float)(result), (exceptmask), (excepts)); \ | ||||
test(fmal, (x), (y), (z), (result), (exceptmask), (excepts)); \ | test(fmal, (x), (y), (z), (result), (exceptmask), (excepts)); \ | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | case FE_TOWARDZERO: | ||||
test(fmal, -LDBL_MIN, LDBL_MIN, 0.0, -0.0, | test(fmal, -LDBL_MIN, LDBL_MIN, 0.0, -0.0, | ||||
ALL_STD_EXCEPT, FE_INEXACT | FE_UNDERFLOW); | ALL_STD_EXCEPT, FE_INEXACT | FE_UNDERFLOW); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
test_infinities(void) | test_infinities(void) | ||||
{ | { | ||||
testall(INFINITY, 1.0, -1.0, INFINITY, ALL_STD_EXCEPT, 0); | testall(INFINITY, 1.0, -1.0, INFINITY, ALL_STD_EXCEPT, 0); | ||||
testall(-1.0, INFINITY, 0.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(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(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, 1.0, INFINITY, ALL_STD_EXCEPT, 0); | testall(INFINITY, INFINITY, 1.0, INFINITY, ALL_STD_EXCEPT, 0); | ||||
Show All 20 Lines | test_infinities(void) | ||||
test(fma, DBL_MAX, -DBL_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, | test(fmal, LDBL_MAX, -LDBL_MAX, INFINITY, INFINITY, | ||||
ALL_STD_EXCEPT, 0); | ALL_STD_EXCEPT, 0); | ||||
} | } | ||||
static void | static void | ||||
test_nans(void) | test_nans(void) | ||||
{ | { | ||||
testall(NAN, 0.0, 0.0, NAN, ALL_STD_EXCEPT, 0); | 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, NAN, 1.0, NAN, ALL_STD_EXCEPT, 0); | ||||
testall(1.0, -1.0, NAN, 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(0.0, 0.0, NAN, NAN, ALL_STD_EXCEPT, 0); | ||||
testall(NAN, NAN, 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. */ | /* x*y should not raise an inexact/overflow/underflow if z is NaN. */ | ||||
testall(M_PI, M_PI, NAN, NAN, ALL_STD_EXCEPT, 0); | testall(M_PI, M_PI, NAN, NAN, ALL_STD_EXCEPT, 0); | ||||
test(fmaf, FLT_MIN, FLT_MIN, 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(fma, DBL_MIN, DBL_MIN, NAN, NAN, ALL_STD_EXCEPT, 0); | ||||
test(fmal, LDBL_MIN, LDBL_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(fmaf, FLT_MAX, FLT_MAX, NAN, NAN, ALL_STD_EXCEPT, 0); | ||||
test(fma, DBL_MAX, DBL_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); | test(fmal, LDBL_MAX, LDBL_MAX, NAN, NAN, ALL_STD_EXCEPT, 0); | ||||
} | } | ||||
/* | /* | ||||
* Tests for cases where z is very small compared to x*y. | * Tests for cases where z is very small compared to x*y. | ||||
*/ | */ | ||||
static void | static void | ||||
test_small_z(void) | test_small_z(void) | ||||
{ | { | ||||
/* x*y positive, z positive */ | /* x*y positive, z positive */ | ||||
if (fegetround() == FE_UPWARD) { | if (fegetround() == FE_UPWARD) { | ||||
test(fmaf, one, one, 0x1.0p-100, 1.0 + FLT_EPSILON, | test(fmaf, one, one, 0x1.0p-100, 1.0 + FLT_EPSILON, | ||||
ALL_STD_EXCEPT, FE_INEXACT); | ALL_STD_EXCEPT, FE_INEXACT); | ||||
test(fma, one, one, 0x1.0p-200, 1.0 + DBL_EPSILON, | test(fma, one, one, 0x1.0p-200, 1.0 + DBL_EPSILON, | ||||
ALL_STD_EXCEPT, FE_INEXACT); | ALL_STD_EXCEPT, FE_INEXACT); | ||||
test(fmal, one, one, 0x1.0p-200, 1.0 + LDBL_EPSILON, | test(fmal, one, one, 0x1.0p-200, 1.0 + LDBL_EPSILON, | ||||
ALL_STD_EXCEPT, FE_INEXACT); | ALL_STD_EXCEPT, FE_INEXACT); | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* Tests for cases where z is very large compared to x*y. | * Tests for cases where z is very large compared to x*y. | ||||
*/ | */ | ||||
static void | static void | ||||
test_big_z(void) | test_big_z(void) | ||||
{ | { | ||||
/* z positive, x*y positive */ | /* z positive, x*y positive */ | ||||
if (fegetround() == FE_UPWARD) { | if (fegetround() == FE_UPWARD) { | ||||
test(fmaf, 0x1.0p-50, 0x1.0p-50, 1.0, 1.0 + FLT_EPSILON, | test(fmaf, 0x1.0p-50, 0x1.0p-50, 1.0, 1.0 + FLT_EPSILON, | ||||
ALL_STD_EXCEPT, FE_INEXACT); | ALL_STD_EXCEPT, FE_INEXACT); | ||||
test(fma, 0x1.0p-100, 0x1.0p-100, 1.0, 1.0 + DBL_EPSILON, | test(fma, 0x1.0p-100, 0x1.0p-100, 1.0, 1.0 + DBL_EPSILON, | ||||
ALL_STD_EXCEPT, FE_INEXACT); | ALL_STD_EXCEPT, FE_INEXACT); | ||||
test(fmal, 0x1.0p-100, 0x1.0p-100, 1.0, 1.0 + LDBL_EPSILON, | test(fmal, 0x1.0p-100, 0x1.0p-100, 1.0, 1.0 + LDBL_EPSILON, | ||||
ALL_STD_EXCEPT, FE_INEXACT); | ALL_STD_EXCEPT, FE_INEXACT); | ||||
▲ Show 20 Lines • Show All 210 Lines • ▼ Show 20 Lines | #elif LDBL_MANT_DIG == 113 | ||||
test(fmal, 0x1.8000000000000000000000000001p+0L, | test(fmal, 0x1.8000000000000000000000000001p+0L, | ||||
0x1.8000000000000000000000000001p+0L, | 0x1.8000000000000000000000000001p+0L, | ||||
-0x1.0000000000000000000000000001p-224L, | -0x1.0000000000000000000000000001p-224L, | ||||
0x1.2000000000000000000000000001p+1L, ALL_STD_EXCEPT, FE_INEXACT); | 0x1.2000000000000000000000000001p+1L, ALL_STD_EXCEPT, FE_INEXACT); | ||||
#endif | #endif | ||||
} | } | ||||
int | static const int rmodes[] = { | ||||
main(void) | FE_TONEAREST, FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO | ||||
{ | }; | ||||
int rmodes[] = { FE_TONEAREST, FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO }; | |||||
unsigned i, j; | |||||
#if defined(__i386__) | ATF_TC_WITHOUT_HEAD(zeroes); | ||||
printf("1..0 # SKIP all testcases fail on i386\n"); | ATF_TC_BODY(zeroes, tc) | ||||
exit(0); | { | ||||
#endif | for (size_t i = 0; i < nitems(rmodes); i++) { | ||||
j = 1; | |||||
printf("1..19\n"); | |||||
for (i = 0; i < nitems(rmodes); i++, j++) { | |||||
printf("rmode = %d\n", rmodes[i]); | printf("rmode = %d\n", rmodes[i]); | ||||
fesetround(rmodes[i]); | fesetround(rmodes[i]); | ||||
test_zeroes(); | test_zeroes(); | ||||
printf("ok %d - fma zeroes\n", j); | |||||
} | } | ||||
} | |||||
for (i = 0; i < nitems(rmodes); i++, j++) { | ATF_TC_WITHOUT_HEAD(infinities); | ||||
ATF_TC_BODY(infinities, tc) | |||||
{ | |||||
#if defined(__amd64__) | #if defined(__amd64__) | ||||
printf("ok %d # SKIP testcase fails assertion on " | if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false)) | ||||
"amd64\n", j); | atf_tc_expect_fail("https://bugs.freebsd.org/205448"); | ||||
continue; | #endif | ||||
#else | for (size_t i = 0; i < nitems(rmodes); i++) { | ||||
printf("rmode = %d\n", rmodes[i]); | printf("rmode = %d\n", rmodes[i]); | ||||
fesetround(rmodes[i]); | fesetround(rmodes[i]); | ||||
test_infinities(); | test_infinities(); | ||||
printf("ok %d - fma infinities\n", j); | |||||
#endif | |||||
} | } | ||||
} | |||||
ATF_TC_WITHOUT_HEAD(nans); | |||||
ATF_TC_BODY(nans, tc) | |||||
{ | |||||
fesetround(FE_TONEAREST); | fesetround(FE_TONEAREST); | ||||
test_nans(); | test_nans(); | ||||
printf("ok %d - fma NaNs\n", j); | } | ||||
j++; | |||||
for (i = 0; i < nitems(rmodes); i++, j++) { | |||||
ATF_TC_WITHOUT_HEAD(small_z); | |||||
ATF_TC_BODY(small_z, tc) | |||||
{ | |||||
for (size_t i = 0; i < nitems(rmodes); i++) { | |||||
printf("rmode = %d\n", rmodes[i]); | printf("rmode = %d\n", rmodes[i]); | ||||
fesetround(rmodes[i]); | fesetround(rmodes[i]); | ||||
test_small_z(); | test_small_z(); | ||||
printf("ok %d - fma small z\n", j); | |||||
} | } | ||||
} | |||||
for (i = 0; i < nitems(rmodes); i++, j++) { | |||||
ATF_TC_WITHOUT_HEAD(big_z); | |||||
ATF_TC_BODY(big_z, tc) | |||||
{ | |||||
for (size_t i = 0; i < nitems(rmodes); i++) { | |||||
printf("rmode = %d\n", rmodes[i]); | printf("rmode = %d\n", rmodes[i]); | ||||
fesetround(rmodes[i]); | fesetround(rmodes[i]); | ||||
test_big_z(); | test_big_z(); | ||||
printf("ok %d - fma big z\n", j); | |||||
} | } | ||||
} | |||||
ATF_TC_WITHOUT_HEAD(accuracy); | |||||
ATF_TC_BODY(accuracy, tc) | |||||
{ | |||||
fesetround(FE_TONEAREST); | fesetround(FE_TONEAREST); | ||||
test_accuracy(); | test_accuracy(); | ||||
printf("ok %d - fma accuracy\n", j); | } | ||||
j++; | |||||
ATF_TC_WITHOUT_HEAD(double_rounding); | |||||
ATF_TC_BODY(double_rounding, tc) { | |||||
test_double_rounding(); | test_double_rounding(); | ||||
printf("ok %d - fma double rounding\n", j); | } | ||||
j++; | |||||
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: | * TODO: | ||||
* - Tests for subnormals | * - Tests for subnormals | ||||
* - Cancellation tests (e.g., z = (double)x*y, but x*y is inexact) | * - Cancellation tests (e.g., z = (double)x*y, but x*y is inexact) | ||||
*/ | */ | ||||
return (atf_no_error()); | |||||
return (0); | |||||
} | } |