Make tst-strtod-underflow type-generic
Carlos O'Donell
codonell@redhat.com
Fri Sep 20 21:55:33 GMT 2024
On 8/19/24 5:15 PM, Joseph Myers wrote:
> The test tst-strtod-underflow covers various edge cases close to the
> underflow threshold for strtod (especially cases where underflow on
> architectures with after-rounding tininess detection depends on the
> rounding mode). Make it use the type-generic machinery, with
> corresponding test inputs for each supported floating-point format, so
> that other functions in the strtod family are tested for underflow
> edge cases as well.
LGTM.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
>
> Tested for x86_64.
>
> 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
> <https://www.gnu.org/licenses/>. */
>
> +/* Defining _LIBC_TEST ensures long double math functions are
> + declared in the headers. */
> +#define _LIBC_TEST 1
> +#define __STDC_WANT_IEC_60559_TYPES_EXT__
OK.
> #include <errno.h>
> #include <fenv.h>
> #include <float.h>
> @@ -25,6 +29,60 @@
> #include <stdlib.h>
> #include <tininess.h>
>
> +#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
OK.
> +
> +#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
OK.
> +
> +#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"
OK.
> +# endif
> +#endif
> +
> +#define _XNTRY(FSUF, FTYPE, FTOSTR, LSUF, CSUF, ...) \
> + CHOOSE_ ## FSUF (__VA_ARGS__),
> +#define XNTRY(...) \
> + GEN_TEST_STRTOD_FOREACH (_XNTRY, __VA_ARGS__)
OK. As defined in tst-strtod.h.
> +
> +#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)
OK.
> 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),
OK. Spot checked some of the tests. 26 before 26 after, but now type-generic.
> };
>
> /* 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); \
OK.
> + 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);
> }
>
OK.
Cheers,
Carlos.
More information about the Libc-alpha
mailing list