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