From af8e7fd7d9091771f12dc6941fed9011d5d732d9 Mon Sep 17 00:00:00 2001 From: riastradh Date: Fri, 3 May 2024 21:40:51 +0000 Subject: [PATCH] tests/lib/libm/t_fe_round.c: Expand nearbyint/rint tests. PR lib/58054 --- tests/lib/libm/t_fe_round.c | 416 +++++++++++++++++++++++++++--------- 1 file changed, 314 insertions(+), 102 deletions(-) diff --git a/tests/lib/libm/t_fe_round.c b/tests/lib/libm/t_fe_round.c index cbd826b672c0..4fd4f8d84ccd 100644 --- a/tests/lib/libm/t_fe_round.c +++ b/tests/lib/libm/t_fe_round.c @@ -41,108 +41,246 @@ static const struct { double input; long int expected; } values[] = { - { FE_DOWNWARD, 3.7, 3}, - { FE_DOWNWARD, -3.7, -4}, - { FE_DOWNWARD, +0, 0}, - { FE_DOWNWARD, -INT-0.01, -INT-1}, - { FE_DOWNWARD, +INT-0.01, INT-1}, - { FE_DOWNWARD, -INT+0.01, -INT}, - { FE_DOWNWARD, +INT+0.01, INT}, + { FE_DOWNWARD, 3.75, 3}, + { FE_DOWNWARD, -3.75, -4}, + { FE_DOWNWARD, +0., 0}, + { FE_DOWNWARD, -INT-0.0625, -INT-1}, + { FE_DOWNWARD, +INT-0.0625, INT-1}, + { FE_DOWNWARD, -INT+0.0625, -INT}, + { FE_DOWNWARD, +INT+0.0625, INT}, #if 0 /* cpu bugs? */ - { FE_DOWNWARD, -0, -1}, + { FE_DOWNWARD, -0., -1}, - { FE_UPWARD, +0, 1}, + { FE_UPWARD, +0., 1}, #endif - { FE_UPWARD, -0, 0}, - { FE_UPWARD, -123.7, -123}, - { FE_UPWARD, 123.999, 124}, - { FE_UPWARD, -INT-0.01, -INT}, - { FE_UPWARD, +INT-0.01, INT}, - { FE_UPWARD, -INT+0.01, -INT+1}, - { FE_UPWARD, +INT+0.01, INT+1}, + { FE_UPWARD, -0., 0}, + { FE_UPWARD, -123.75, -123}, + { FE_UPWARD, 123.75, 124}, + { FE_UPWARD, -INT-0.0625, -INT}, + { FE_UPWARD, +INT-0.0625, INT}, + { FE_UPWARD, -INT+0.0625, -INT+1}, + { FE_UPWARD, +INT+0.0625, INT+1}, - { FE_TOWARDZERO, 1.99, 1}, - { FE_TOWARDZERO, -1.99, -1}, - { FE_TOWARDZERO, 0.2, 0}, - { FE_TOWARDZERO, INT+0.01, INT}, - { FE_TOWARDZERO, INT-0.01, INT - 1}, - { FE_TOWARDZERO, -INT+0.01, -INT + 1}, - { FE_TOWARDZERO, +0, 0}, - { FE_TOWARDZERO, -0, 0}, + { FE_TOWARDZERO, 1.9375, 1}, + { FE_TOWARDZERO, -1.9375, -1}, + { FE_TOWARDZERO, 0.25, 0}, + { FE_TOWARDZERO, INT+0.0625, INT}, + { FE_TOWARDZERO, INT-0.0625, INT - 1}, + { FE_TOWARDZERO, -INT+0.0625, -INT + 1}, + { FE_TOWARDZERO, +0., 0}, + { FE_TOWARDZERO, -0., 0}, - { FE_TONEAREST, -INT-0.01, -INT}, - { FE_TONEAREST, +INT-0.01, INT}, - { FE_TONEAREST, -INT+0.01, -INT}, - { FE_TONEAREST, +INT+0.01, INT}, - { FE_TONEAREST, -INT-0.501, -INT-1}, - { FE_TONEAREST, +INT-0.501, INT-1}, - { FE_TONEAREST, -INT+0.501, -INT+1}, - { FE_TONEAREST, +INT+0.501, INT+1}, - { FE_TONEAREST, +0, 0}, - { FE_TONEAREST, -0, 0}, + { FE_TONEAREST, -INT-0.0625, -INT}, + { FE_TONEAREST, +INT-0.0625, INT}, + { FE_TONEAREST, -INT+0.0625, -INT}, + { FE_TONEAREST, +INT+0.0625, INT}, + { FE_TONEAREST, -INT-0.53125, -INT-1}, + { FE_TONEAREST, +INT-0.53125, INT-1}, + { FE_TONEAREST, -INT+0.53125, -INT+1}, + { FE_TONEAREST, +INT+0.53125, INT+1}, + { FE_TONEAREST, +0., 0}, + { FE_TONEAREST, -0., 0}, }; -ATF_TC(fe_round); -ATF_TC_HEAD(fe_round, tc) +ATF_TC(fe_lrint); +ATF_TC_HEAD(fe_lrint, tc) { - atf_tc_set_md_var(tc, "descr","Checking IEEE 754 rounding modes using lrint"); + atf_tc_set_md_var(tc, "descr", + "Checking IEEE 754 rounding modes using lrint(3)"); } -ATF_TC_BODY(fe_round, tc) +ATF_TC_BODY(fe_lrint, tc) { + enum { + LLRINT, + LLRINTF, + LRINT, + LRINTF, + N_FN, + } fn; + static const char *const fnname[] = { + [LLRINT] = "llrint", + [LLRINTF] = "llrintf", + [LRINT] = "lrint", + [LRINTF] = "lrintf", + }; long int received; + unsigned i; - for (unsigned int i = 0; i < __arraycount(values); i++) { - fesetround(values[i].round_mode); + for (i = 0; i < __arraycount(values); i++) { + for (fn = 0; fn < N_FN; fn++) { + /* + * Set the requested rounding mode. + */ + fesetround(values[i].round_mode); - received = lrint(values[i].input); - ATF_CHECK_MSG( - (labs(received - values[i].expected) < EPSILON), - "lrint rounding wrong, difference too large\n" - "input: %f (index %d): got %ld, expected %ld\n", - values[i].input, i, received, values[i].expected); + /* + * Call the lrint(3)-family function. + */ + switch (fn) { + case LLRINT: + received = llrint(values[i].input); + break; + case LLRINTF: + received = llrintf(values[i].input); + break; + case LRINT: + received = lrint(values[i].input); + break; + case LRINTF: + received = lrintf(values[i].input); + break; + default: + atf_tc_fail("impossible"); + } - /* Do we get the same rounding mode out? */ - ATF_CHECK_MSG( - (fegetround() == values[i].round_mode), - "Didn't get the same rounding mode out!\n" - "(index %d) fed in %d rounding mode, got %d out\n", - i, values[i].round_mode, fegetround()); + /* + * Assuming the result we got has zero + * fractional part, casting to long int should + * have no rounding. Verify it matches the + * integer we expect. + */ + ATF_CHECK_MSG((long int)received == values[i].expected, + "[%u] %s %s(%f): got %ld, expected %ld", + i, rmname(values[i].round_mode), fnname[fn], + values[i].input, + (long int)received, values[i].expected); + + /* Do we get the same rounding mode out? */ + ATF_CHECK_MSG(fegetround() == values[i].round_mode, + "[%u] %s: set %d (%s), got %d (%s)", + i, fnname[fn], + values[i].round_mode, rmname(values[i].round_mode), + fegetround(), rmname(fegetround())); + } } } -ATF_TC(fe_nearbyint); -ATF_TC_HEAD(fe_nearbyint, tc) +ATF_TC(fe_nearbyint_rint); +ATF_TC_HEAD(fe_nearbyint_rint, tc) { atf_tc_set_md_var(tc, "descr", - "Checking IEEE 754 rounding modes using nearbyint"); + "Checking IEEE 754 rounding modes using nearbyint/rint"); } -ATF_TC_BODY(fe_nearbyint, tc) +ATF_TC_BODY(fe_nearbyint_rint, tc) { + enum { + NEARBYINT, + NEARBYINTF, + NEARBYINTL, + RINT, + RINTF, + RINTL, + N_FN, + } fn; + static const char *const fnname[] = { + [NEARBYINT] = "nearbyint", + [NEARBYINTF] = "nearbyintf", + [NEARBYINTL] = "nearbyintl", + [RINT] = "rint", + [RINTF] = "rintf", + [RINTL] = "rintl", + }; double received, ipart, fpart; + unsigned i; - for (unsigned int i = 0; i < __arraycount(values); i++) { - fesetround(values[i].round_mode); + for (i = 0; i < __arraycount(values); i++) { + for (fn = 0; fn < N_FN; fn++) { + bool expect_except = + values[i].input != (double)values[i].expected; - received = nearbyint(values[i].input); - fpart = modf(received, &ipart); - ATF_CHECK_MSG(fpart == 0, - "[%u] %s nearbyint(%f) has fractional part %f" - " (integer part %f)", - i, rmname(values[i].round_mode), values[i].input, fpart, - ipart); - ATF_CHECK_MSG((long int)received == values[i].expected, - "[%u] %s nearbyint(%f): got %f, expected %ld", - i, rmname(values[i].round_mode), - values[i].input, received, values[i].expected); + /* + * Set the requested rounding mode. + */ + fesetround(values[i].round_mode); - /* Do we get the same rounding mode out? */ - ATF_CHECK_MSG(fegetround() == values[i].round_mode, - "[%u] set %d (%s), got %d (%s)", - i, - values[i].round_mode, rmname(values[i].round_mode), - fegetround(), rmname(fegetround())); + /* + * Clear sticky floating-point exception bits + * so we can verify whether the FE_INEXACT + * exception is raised. + */ + feclearexcept(FE_ALL_EXCEPT); + + /* + * Call the rint(3)-family function. + */ + switch (fn) { + case NEARBYINT: + received = nearbyint(values[i].input); + expect_except = false; + break; + case NEARBYINTF: + received = nearbyintf(values[i].input); + expect_except = false; + break; + case NEARBYINTL: + received = nearbyintl(values[i].input); + expect_except = false; + break; + case RINT: + received = rint(values[i].input); + break; + case RINTF: + received = rintf(values[i].input); + break; + case RINTL: + received = rintl(values[i].input); + break; + default: + atf_tc_fail("impossible"); + } + + /* + * Verify FE_INEXACT was raised or not, + * depending on whether there was rounding and + * whether the function is supposed to raise + * exceptions. + */ + if (expect_except) { + ATF_CHECK_MSG(fetestexcept(FE_INEXACT) != 0, + "[%u] %s %s(%f)" + " failed to raise FE_INEXACT", + i, rmname(values[i].round_mode), + fnname[fn], values[i].input); + } else { + ATF_CHECK_MSG(fetestexcept(FE_INEXACT) == 0, + "[%u] %s %s(%f)" + " spuriously raised FE_INEXACT", + i, rmname(values[i].round_mode), + fnname[fn], values[i].input); + } + + /* + * Verify the fractional part of the result is + * zero -- the result of rounding to an integer + * is supposed to be an integer. + */ + fpart = modf(received, &ipart); + ATF_CHECK_MSG(fpart == 0, + "[%u] %s %s(%f)=%f has fractional part %f" + " (integer part %f)", + i, rmname(values[i].round_mode), fnname[fn], + values[i].input, received, fpart, ipart); + + /* + * Assuming the result we got has zero + * fractional part, casting to long int should + * have no rounding. Verify it matches the + * integer we expect. + */ + ATF_CHECK_MSG((long int)received == values[i].expected, + "[%u] %s %s(%f): got %f, expected %ld", + i, rmname(values[i].round_mode), fnname[fn], + values[i].input, received, values[i].expected); + + /* Do we get the same rounding mode out? */ + ATF_CHECK_MSG(fegetround() == values[i].round_mode, + "[%u] %s: set %d (%s), got %d (%s)", + i, fnname[fn], + values[i].round_mode, rmname(values[i].round_mode), + fegetround(), rmname(fegetround())); + } } } @@ -154,7 +292,7 @@ ATF_TC_BODY(fe_nearbyint, tc) static const struct { int round_mode; long double input; - intmax_t expected; + int64_t expected; } valuesl[] = { { FE_TOWARDZERO, 0x2.00000000000008p+52L, 0x20000000000000 }, { FE_DOWNWARD, 0x2.00000000000008p+52L, 0x20000000000000 }, @@ -166,38 +304,112 @@ static const struct { { FE_TONEAREST, 0x2.00000000000018p+52L, 0x20000000000002 }, }; -ATF_TC(fe_nearbyintl); -ATF_TC_HEAD(fe_nearbyintl, tc) +ATF_TC(fe_nearbyintl_rintl); +ATF_TC_HEAD(fe_nearbyintl_rintl, tc) { atf_tc_set_md_var(tc, "descr", - "Checking IEEE 754 rounding modes using nearbyintl"); + "Checking IEEE 754 rounding modes using nearbyintl/rintl"); } -ATF_TC_BODY(fe_nearbyintl, tc) +ATF_TC_BODY(fe_nearbyintl_rintl, tc) { + enum { + RINTL, + NEARBYINTL, + N_FN, + } fn; + static const char *const fnname[] = { + [RINTL] = "rintl", + [NEARBYINTL] = "nearbyintl", + }; long double received, ipart, fpart; + unsigned i; - for (unsigned int i = 0; i < __arraycount(valuesl); i++) { - fesetround(valuesl[i].round_mode); + for (i = 0; i < __arraycount(valuesl); i++) { + for (fn = 0; fn < N_FN; fn++) { + bool expect_except = + (valuesl[i].input != + (long double)valuesl[i].expected); - received = nearbyintl(valuesl[i].input); - fpart = modfl(received, &ipart); - ATF_CHECK_MSG(fpart == 0, - "[%u] %s nearbyintl(%Lf) has fractional part %Lf" - " (integer part %Lf)", - i, rmname(valuesl[i].round_mode), valuesl[i].input, fpart, - ipart); - ATF_CHECK_MSG((intmax_t)received == valuesl[i].expected, - "[%u] %s nearbyintl(%Lf): got %Lf, expected %jd", - i, rmname(valuesl[i].round_mode), - valuesl[i].input, received, valuesl[i].expected); + /* + * Set the requested rounding mode. + */ + fesetround(valuesl[i].round_mode); - /* Do we get the same rounding mode out? */ - ATF_CHECK_MSG(fegetround() == valuesl[i].round_mode, - "[%u] set %d (%s), got %d (%s)", - i, - valuesl[i].round_mode, rmname(valuesl[i].round_mode), - fegetround(), rmname(fegetround())); + /* + * Clear sticky floating-point exception bits + * so we can verify whether the FE_INEXACT + * exception is raised. + */ + feclearexcept(FE_ALL_EXCEPT); + + /* + * Call the rint(3)-family function. + */ + switch (fn) { + case NEARBYINTL: + received = nearbyintl(valuesl[i].input); + expect_except = false; + break; + case RINTL: + received = rintl(valuesl[i].input); + break; + default: + atf_tc_fail("impossible"); + } + + /* + * Verify FE_INEXACT was raised or not, + * depending on whether there was rounding and + * whether the function is supposed to raise + * exceptions. + */ + if (expect_except) { + ATF_CHECK_MSG(fetestexcept(FE_INEXACT) != 0, + "[%u] %s %s(%Lf)" + " failed to raise FE_INEXACT", + i, rmname(valuesl[i].round_mode), + fnname[fn], valuesl[i].input); + } else { + ATF_CHECK_MSG(fetestexcept(FE_INEXACT) == 0, + "[%u] %s %s(%Lf)" + " spuriously raised FE_INEXACT", + i, rmname(valuesl[i].round_mode), + fnname[fn], valuesl[i].input); + } + + /* + * Verify the fractional part of the result is + * zero -- the result of rounding to an integer + * is supposed to be an integer. + */ + fpart = modfl(received, &ipart); + ATF_CHECK_MSG(fpart == 0, + "[%u] %s %s(%Lf)=%Lf has fractional part %Lf" + " (integer part %Lf)", + i, rmname(valuesl[i].round_mode), fnname[fn], + valuesl[i].input, received, fpart, ipart); + + /* + * Assuming the result we got has zero + * fractional part, casting to int64_t should + * have no rounding. Verify it matches the + * integer we expect. + */ + ATF_CHECK_MSG(((int64_t)received == + valuesl[i].expected), + "[%u] %s %s(%Lf): got %Lf, expected %jd", + i, rmname(valuesl[i].round_mode), fnname[fn], + valuesl[i].input, received, valuesl[i].expected); + + /* Do we get the same rounding mode out? */ + ATF_CHECK_MSG(fegetround() == valuesl[i].round_mode, + "[%u] %s: set %d (%s), got %d (%s)", + i, fnname[fn], + valuesl[i].round_mode, + rmname(valuesl[i].round_mode), + fegetround(), rmname(fegetround())); + } } } @@ -267,10 +479,10 @@ ATF_TC_BODY(fe_nexttoward, tc) ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, fe_round); - ATF_TP_ADD_TC(tp, fe_nearbyint); + ATF_TP_ADD_TC(tp, fe_lrint); + ATF_TP_ADD_TC(tp, fe_nearbyint_rint); #ifdef __HAVE_LONG_DOUBLE - ATF_TP_ADD_TC(tp, fe_nearbyintl); + ATF_TP_ADD_TC(tp, fe_nearbyintl_rintl); #endif ATF_TP_ADD_TC(tp, fe_nextafter); ATF_TP_ADD_TC(tp, fe_nexttoward);