about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJoseph Myers <josmyers@redhat.com>2024-09-20 23:25:32 +0000
committerArjun Shankar <arjun@redhat.com>2024-10-18 18:57:47 +0200
commitb3c51635efb532dd0544e6f4dedd6a184f0ae1a2 (patch)
tree14b50c6721e749e89e8c117ab76e0526125040a8
parentb74be22f65c017c6f980e608127c260497d7f3c2 (diff)
downloadglibc-b3c51635efb532dd0544e6f4dedd6a184f0ae1a2.tar.gz
glibc-b3c51635efb532dd0544e6f4dedd6a184f0ae1a2.tar.xz
glibc-b3c51635efb532dd0544e6f4dedd6a184f0ae1a2.zip
Make tst-strtod-underflow type-generic
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.

Tested for x86_64.

(cherry picked from commit 94ca2c0894f0e1b62625c369cc598a2b9236622c)
-rw-r--r--stdlib/tst-strtod-underflow.c357
1 files changed, 297 insertions, 60 deletions
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__
 #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
+
+#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);
 	}