diff --git a/stdlib/tst-strtod-underflow.c b/stdlib/tst-strtod-underflow.c index a5ced18599..8598b95b6d 100644 --- a/stdlib/tst-strtod-underflow.c +++ b/stdlib/tst-strtod-underflow.c @@ -17,6 +17,10 @@ License along with the GNU C Library; if not, see . */ +/* Defining _LIBC_TEST ensures long double math functions are + declared in the headers. */ +#define _LIBC_TEST 1 +#define __STDC_WANT_IEC_60559_TYPES_EXT__ #include #include #include @@ -25,6 +29,60 @@ #include #include +#include "tst-strtod.h" + +/* Logic for selecting between tests for different formats is as in + tst-strtod-skeleton.c, but here it is selecting string inputs with + different underflow properties, rather than generated test + data. */ + +#define _CONCAT(a, b) a ## b +#define CONCAT(a, b) _CONCAT (a, b) + +#define MEMBER(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \ + const char *s_ ## FSUF; + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +# define CHOOSE_ld(f,d,...) d +#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && LDBL_MIN_EXP == -16381 +# define CHOOSE_ld(f,d,ld64i,...) ld64i +#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && LDBL_MIN_EXP == -16382 +# define CHOOSE_ld(f,d,ld64i,ld64m,...) ld64m +#elif LDBL_MANT_DIG == 106 && LDBL_MAX_EXP == 1024 +# define CHOOSE_ld(f,d,ld64i,ld64m,ld106,...) ld106 +#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 +# define CHOOSE_ld(f,d,ld64i,ld64m,ld106,ld113,...) ld113 +#else +# error "unknown long double format" +#endif + +#define CHOOSE_f(f,...) f +#define CHOOSE_f32(f,...) f +#define CHOOSE_d(f,d,...) d +#define CHOOSE_f64(f,d,...) d +#define CHOOSE_f32x(f,d,...) d +#define CHOOSE_f128(f,d,ld64i,ld64m,ld106,ld113,...) ld113 + +#if __HAVE_FLOAT64X +# if FLT64X_MANT_DIG == 113 && FLT64X_MAX_EXP == 16384 +# define CHOOSE_f64x(f,d,ld64i,ld64m,ld106,ld113,...) ld113 +# elif (FLT64X_MANT_DIG == 64 \ + && FLT64X_MAX_EXP == 16384 \ + && FLT64X_MIN_EXP == -16381) +# define CHOOSE_f64x(f,d,ld64i,...) ld64i +# else +# error "unknown _Float64x format" +# endif +#endif + +#define _XNTRY(FSUF, FTYPE, FTOSTR, LSUF, CSUF, ...) \ + CHOOSE_ ## FSUF (__VA_ARGS__), +#define XNTRY(...) \ + GEN_TEST_STRTOD_FOREACH (_XNTRY, __VA_ARGS__) + +#define TEST(f, d, ld64i, ld64m, ld106, ld113, u) \ + { XNTRY(f, d, ld64i, ld64m, ld106, ld113) u } + enum underflow_case { /* Result is exact or outside the subnormal range. */ @@ -55,38 +113,194 @@ enum underflow_case struct test { - const char *s; + GEN_TEST_STRTOD_FOREACH (MEMBER) enum underflow_case c; }; static const struct test tests[] = { - { "0x1p-1022", UNDERFLOW_NONE }, - { "-0x1p-1022", UNDERFLOW_NONE }, - { "0x0p-10000000000000000000000000", UNDERFLOW_NONE }, - { "-0x0p-10000000000000000000000000", UNDERFLOW_NONE }, - { "0x1p-10000000000000000000000000", UNDERFLOW_ALWAYS }, - { "-0x1p-10000000000000000000000000", UNDERFLOW_ALWAYS }, - { "0x1.000000000000000000001p-1022", UNDERFLOW_NONE }, - { "-0x1.000000000000000000001p-1022", UNDERFLOW_NONE }, - { "0x1p-1075", UNDERFLOW_ALWAYS }, - { "-0x1p-1075", UNDERFLOW_ALWAYS }, - { "0x1p-1023", UNDERFLOW_NONE }, - { "-0x1p-1023", UNDERFLOW_NONE }, - { "0x1p-1074", UNDERFLOW_NONE }, - { "-0x1p-1074", UNDERFLOW_NONE }, - { "0x1.ffffffffffffep-1023", UNDERFLOW_NONE }, - { "-0x1.ffffffffffffep-1023", UNDERFLOW_NONE }, - { "0x1.fffffffffffffp-1023", UNDERFLOW_ALWAYS }, - { "-0x1.fffffffffffffp-1023", UNDERFLOW_ALWAYS }, - { "0x1.fffffffffffff0001p-1023", UNDERFLOW_EXCEPT_UPWARD }, - { "-0x1.fffffffffffff0001p-1023", UNDERFLOW_EXCEPT_DOWNWARD }, - { "0x1.fffffffffffff7fffp-1023", UNDERFLOW_EXCEPT_UPWARD }, - { "-0x1.fffffffffffff7fffp-1023", UNDERFLOW_EXCEPT_DOWNWARD }, - { "0x1.fffffffffffff8p-1023", UNDERFLOW_ONLY_DOWNWARD_ZERO }, - { "-0x1.fffffffffffff8p-1023", UNDERFLOW_ONLY_UPWARD_ZERO }, - { "0x1.fffffffffffffffffp-1023", UNDERFLOW_ONLY_DOWNWARD_ZERO }, - { "-0x1.fffffffffffffffffp-1023", UNDERFLOW_ONLY_UPWARD_ZERO }, + TEST ("0x1p-126", + "0x1p-1022", + "0x1p-16382", + "0x1p-16383", + "0x1p-969", + "0x1p-16382", + UNDERFLOW_NONE), + TEST ("-0x1p-126", + "-0x1p-1022", + "-0x1p-16382", + "-0x1p-16383", + "-0x1p-969", + "-0x1p-16382", + UNDERFLOW_NONE), + TEST ("0x0p-10000000000000000000000000", + "0x0p-10000000000000000000000000", + "0x0p-10000000000000000000000000", + "0x0p-10000000000000000000000000", + "0x0p-10000000000000000000000000", + "0x0p-10000000000000000000000000", + UNDERFLOW_NONE), + TEST ("-0x0p-10000000000000000000000000", + "-0x0p-10000000000000000000000000", + "-0x0p-10000000000000000000000000", + "-0x0p-10000000000000000000000000", + "-0x0p-10000000000000000000000000", + "-0x0p-10000000000000000000000000", + UNDERFLOW_NONE), + TEST ("0x1p-10000000000000000000000000", + "0x1p-10000000000000000000000000", + "0x1p-10000000000000000000000000", + "0x1p-10000000000000000000000000", + "0x1p-10000000000000000000000000", + "0x1p-10000000000000000000000000", + UNDERFLOW_ALWAYS), + TEST ("-0x1p-10000000000000000000000000", + "-0x1p-10000000000000000000000000", + "-0x1p-10000000000000000000000000", + "-0x1p-10000000000000000000000000", + "-0x1p-10000000000000000000000000", + "-0x1p-10000000000000000000000000", + UNDERFLOW_ALWAYS), + TEST ("0x1.000000000000000000001p-126", + "0x1.000000000000000000001p-1022", + "0x1.000000000000000000001p-16382", + "0x1.000000000000000000001p-16383", + "0x1.000000000000000000001p-969", + "0x1.00000000000000000000000000000000000000001p-16382", + UNDERFLOW_NONE), + TEST ("-0x1.000000000000000000001p-126", + "-0x1.000000000000000000001p-1022", + "-0x1.000000000000000000001p-16382", + "-0x1.000000000000000000001p-16383", + "-0x1.000000000000000000001p-969", + "-0x1.00000000000000000000000000000000000000001p-16382", + UNDERFLOW_NONE), + TEST ("0x1p-150", + "0x1p-1075", + "0x1p-16446", + "0x1p-16447", + "0x1p-1075", + "0x1p-16495", + UNDERFLOW_ALWAYS), + TEST ("-0x1p-150", + "-0x1p-1075", + "-0x1p-16446", + "-0x1p-16447", + "-0x1p-1075", + "-0x1p-16495", + UNDERFLOW_ALWAYS), + TEST ("0x1p-127", + "0x1p-1023", + "0x1p-16383", + "0x1p-16384", + "0x1p-970", + "0x1p-16383", + UNDERFLOW_NONE), + TEST ("-0x1p-127", + "-0x1p-1023", + "-0x1p-16383", + "-0x1p-16384", + "-0x1p-970", + "-0x1p-16383", + UNDERFLOW_NONE), + TEST ("0x1p-149", + "0x1p-1074", + "0x1p-16445", + "0x1p-16446", + "0x1p-1074", + "0x1p-16494", + UNDERFLOW_NONE), + TEST ("-0x1p-149", + "-0x1p-1074", + "-0x1p-16445", + "-0x1p-16446", + "-0x1p-1074", + "-0x1p-16494", + UNDERFLOW_NONE), + TEST ("0x1.fffffcp-127", + "0x1.ffffffffffffep-1023", + "0x1.fffffffffffffffcp-16383", + "0x1.fffffffffffffffcp-16384", + "0x1.ffffffffffffffffffffffffffp-970", + "0x1.fffffffffffffffffffffffffffep-16383", + UNDERFLOW_NONE), + TEST ("-0x1.fffffcp-127", + "-0x1.ffffffffffffep-1023", + "-0x1.fffffffffffffffcp-16383", + "-0x1.fffffffffffffffcp-16384", + "-0x1.ffffffffffffffffffffffffffp-970", + "-0x1.fffffffffffffffffffffffffffep-16383", + UNDERFLOW_NONE), + TEST ("0x1.fffffep-127", + "0x1.fffffffffffffp-1023", + "0x1.fffffffffffffffep-16383", + "0x1.fffffffffffffffep-16384", + "0x1.ffffffffffffffffffffffffff8p-970", + "0x1.ffffffffffffffffffffffffffffp-16383", + UNDERFLOW_ALWAYS), + TEST ("-0x1.fffffep-127", + "-0x1.fffffffffffffp-1023", + "-0x1.fffffffffffffffep-16383", + "-0x1.fffffffffffffffep-16384", + "-0x1.ffffffffffffffffffffffffff8p-970", + "-0x1.ffffffffffffffffffffffffffffp-16383", + UNDERFLOW_ALWAYS), + TEST ("0x1.fffffe0001p-127", + "0x1.fffffffffffff0001p-1023", + "0x1.fffffffffffffffe0001p-16383", + "0x1.fffffffffffffffe0001p-16384", + "0x1.ffffffffffffffffffffffffff80001p-970", + "0x1.ffffffffffffffffffffffffffff0001p-16383", + UNDERFLOW_EXCEPT_UPWARD), + TEST ("-0x1.fffffe0001p-127", + "-0x1.fffffffffffff0001p-1023", + "-0x1.fffffffffffffffe0001p-16383", + "-0x1.fffffffffffffffe0001p-16384", + "-0x1.ffffffffffffffffffffffffff80001p-970", + "-0x1.ffffffffffffffffffffffffffff0001p-16383", + UNDERFLOW_EXCEPT_DOWNWARD), + TEST ("0x1.fffffeffffp-127", + "0x1.fffffffffffff7fffp-1023", + "0x1.fffffffffffffffeffffp-16383", + "0x1.fffffffffffffffeffffp-16384", + "0x1.ffffffffffffffffffffffffffbffffp-970", + "0x1.ffffffffffffffffffffffffffff7fffp-16383", + UNDERFLOW_EXCEPT_UPWARD), + TEST ("-0x1.fffffeffffp-127", + "-0x1.fffffffffffff7fffp-1023", + "-0x1.fffffffffffffffeffffp-16383", + "-0x1.fffffffffffffffeffffp-16384", + "-0x1.ffffffffffffffffffffffffffbffffp-970", + "-0x1.ffffffffffffffffffffffffffff7fffp-16383", + UNDERFLOW_EXCEPT_DOWNWARD), + TEST ("0x1.ffffffp-127", + "0x1.fffffffffffff8p-1023", + "0x1.ffffffffffffffffp-16383", + "0x1.ffffffffffffffffp-16384", + "0x1.ffffffffffffffffffffffffffcp-970", + "0x1.ffffffffffffffffffffffffffff8p-16383", + UNDERFLOW_ONLY_DOWNWARD_ZERO), + TEST ("-0x1.ffffffp-127", + "-0x1.fffffffffffff8p-1023", + "-0x1.ffffffffffffffffp-16383", + "-0x1.ffffffffffffffffp-16384", + "-0x1.ffffffffffffffffffffffffffcp-970", + "-0x1.ffffffffffffffffffffffffffff8p-16383", + UNDERFLOW_ONLY_UPWARD_ZERO), + TEST ("0x1.ffffffffffp-127", + "0x1.fffffffffffffffffp-1023", + "0x1.ffffffffffffffffffffp-16383", + "0x1.ffffffffffffffffffffp-16384", + "0x1.ffffffffffffffffffffffffffffffp-970", + "0x1.ffffffffffffffffffffffffffffffffp-16383", + UNDERFLOW_ONLY_DOWNWARD_ZERO), + TEST ("-0x1.ffffffffffp-127", + "-0x1.fffffffffffffffffp-1023", + "-0x1.ffffffffffffffffffffp-16383", + "-0x1.ffffffffffffffffffffp-16384", + "-0x1.ffffffffffffffffffffffffffffffp-970", + "-0x1.ffffffffffffffffffffffffffffffffp-16383", + UNDERFLOW_ONLY_UPWARD_ZERO), }; /* Return whether to expect underflow from a particular testcase, in a @@ -133,39 +347,62 @@ static bool support_underflow_exception = false; volatile double d = DBL_MIN; volatile double dd; -static int -test_in_one_mode (const char *s, enum underflow_case c, int rm, - const char *mode_name) +static bool +test_got_fe_underflow (void) { - int result = 0; - feclearexcept (FE_ALL_EXCEPT); - errno = 0; - double d = strtod (s, NULL); - int got_errno = errno; #ifdef FE_UNDERFLOW - bool got_fe_underflow = fetestexcept (FE_UNDERFLOW) != 0; + return fetestexcept (FE_UNDERFLOW) != 0; #else - bool got_fe_underflow = false; + return false; #endif - printf ("strtod (%s) (%s) returned %a, errno = %d, %sunderflow exception\n", - s, mode_name, d, got_errno, got_fe_underflow ? "" : "no "); - bool this_expect_underflow = expect_underflow (c, rm); - if (got_errno != 0 && got_errno != ERANGE) - { - puts ("FAIL: errno neither 0 nor ERANGE"); - result = 1; - } - else if (this_expect_underflow != (errno == ERANGE)) - { - puts ("FAIL: underflow from errno differs from expectations"); - result = 1; - } - if (support_underflow_exception && got_fe_underflow != this_expect_underflow) - { - puts ("FAIL: underflow from exceptions differs from expectations"); - result = 1; - } - return result; +} + +#define TEST_STRTOD(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \ +static int \ +test_strto ## FSUF (int i, int rm, const char *mode_name) \ +{ \ + const char *s = tests[i].s_ ## FSUF; \ + enum underflow_case c = tests[i].c; \ + int result = 0; \ + feclearexcept (FE_ALL_EXCEPT); \ + errno = 0; \ + FTYPE d = strto ## FSUF (s, NULL); \ + int got_errno = errno; \ + bool got_fe_underflow = test_got_fe_underflow (); \ + char buf[FSTRLENMAX]; \ + FTOSTR (buf, sizeof (buf), "%a", d); \ + printf ("strto" #FSUF \ + " (%s) (%s) returned %s, errno = %d, " \ + "%sunderflow exception\n", \ + s, mode_name, buf, got_errno, \ + got_fe_underflow ? "" : "no "); \ + bool this_expect_underflow = expect_underflow (c, rm); \ + if (got_errno != 0 && got_errno != ERANGE) \ + { \ + puts ("FAIL: errno neither 0 nor ERANGE"); \ + result = 1; \ + } \ + else if (this_expect_underflow != (errno == ERANGE)) \ + { \ + puts ("FAIL: underflow from errno differs from expectations"); \ + result = 1; \ + } \ + if (support_underflow_exception \ + && got_fe_underflow != this_expect_underflow) \ + { \ + puts ("FAIL: underflow from exceptions " \ + "differs from expectations"); \ + result = 1; \ + } \ + return result; \ +} + +GEN_TEST_STRTOD_FOREACH (TEST_STRTOD) + +static int +test_in_one_mode (size_t i, int rm, const char *mode_name) +{ + return STRTOD_TEST_FOREACH (test_strto, i, rm, mode_name); } static int @@ -191,12 +428,12 @@ do_test (void) #endif for (size_t i = 0; i < sizeof (tests) / sizeof (tests[0]); i++) { - result |= test_in_one_mode (tests[i].s, tests[i].c, fe_tonearest, + result |= test_in_one_mode (i, fe_tonearest, "default rounding mode"); #ifdef FE_DOWNWARD if (!fesetround (FE_DOWNWARD)) { - result |= test_in_one_mode (tests[i].s, tests[i].c, FE_DOWNWARD, + result |= test_in_one_mode (i, FE_DOWNWARD, "FE_DOWNWARD"); fesetround (save_round_mode); } @@ -204,7 +441,7 @@ do_test (void) #ifdef FE_TOWARDZERO if (!fesetround (FE_TOWARDZERO)) { - result |= test_in_one_mode (tests[i].s, tests[i].c, FE_TOWARDZERO, + result |= test_in_one_mode (i, FE_TOWARDZERO, "FE_TOWARDZERO"); fesetround (save_round_mode); } @@ -212,7 +449,7 @@ do_test (void) #ifdef FE_UPWARD if (!fesetround (FE_UPWARD)) { - result |= test_in_one_mode (tests[i].s, tests[i].c, FE_UPWARD, + result |= test_in_one_mode (i, FE_UPWARD, "FE_UPWARD"); fesetround (save_round_mode); }