More thoroughly test underflow / errno in tst-strtod-round

Add tests of underflow in tst-strtod-round, and thus also test for
errno being unchanged when there is neither overflow nor underflow.
The errno setting before the function call to test for being unchanged
is adjusted to set errno to 12345 instead of 0, so that any bugs where
strtod sets errno to 0 would be detected.

This doesn't add any new test inputs for tst-strtod-round, and in
particular doesn't cover the edge cases of underflow the way
tst-strtod-underflow does (none of the existing test inputs for
tst-strtod-round actually exercise cases that have underflow with
before-rounding tininess detection but not with after-rounding
tininess detection), but at least it provides some coverage (as per
the recent discussions) that ordinary non-overflowing non-underflowing
inputs to these functions do not set errno.

Tested for x86_64.
This commit is contained in:
Joseph Myers 2024-08-27 12:38:01 +00:00
parent 3de73f974f
commit d73ed2601b
3 changed files with 6522 additions and 6426 deletions

View File

@ -46,6 +46,7 @@ static int
string_to_fp (mpfr_t f, const char *s, mpfr_rnd_t rnd) string_to_fp (mpfr_t f, const char *s, mpfr_rnd_t rnd)
{ {
mpfr_clear_overflow (); mpfr_clear_overflow ();
mpfr_clear_underflow ();
#ifdef WORKAROUND #ifdef WORKAROUND
mpfr_t f2; mpfr_t f2;
mpfr_init2 (f2, 100000); mpfr_init2 (f2, 100000);
@ -53,12 +54,16 @@ string_to_fp (mpfr_t f, const char *s, mpfr_rnd_t rnd)
int r = mpfr_set (f, f2, rnd); int r = mpfr_set (f, f2, rnd);
r |= mpfr_subnormalize (f, r, rnd); r |= mpfr_subnormalize (f, r, rnd);
mpfr_clear (f2); mpfr_clear (f2);
return r0 | r; r |= r0;
#else #else
int r = mpfr_strtofr (f, s, NULL, 0, rnd); int r = mpfr_strtofr (f, s, NULL, 0, rnd);
r |= mpfr_subnormalize (f, r, rnd); r |= mpfr_subnormalize (f, r, rnd);
return r;
#endif #endif
if (r == 0)
/* The MPFR underflow flag is set for exact subnormal results,
which is not wanted here. */
mpfr_clear_underflow ();
return r;
} }
void void
@ -70,6 +75,21 @@ print_fp (FILE *fout, mpfr_t f, const char *suffix)
mpfr_fprintf (fout, "\t%Ra%s", f, suffix); mpfr_fprintf (fout, "\t%Ra%s", f, suffix);
} }
static const char *
suffix_to_print (bool overflow, bool underflow, bool underflow_before_rounding,
bool with_comma)
{
if (overflow)
return with_comma ? ", true, false,\n" : ", true, false";
if (underflow)
return with_comma ? ", false, true,\n" : ", false, true";
if (underflow_before_rounding)
return (with_comma
? ", false, !TININESS_AFTER_ROUNDING,\n"
: ", false, !TININESS_AFTER_ROUNDING");
return with_comma ? ", false, false,\n" : ", false, false";
}
static void static void
round_str (FILE *fout, const char *s, int prec, int emin, int emax, round_str (FILE *fout, const char *s, int prec, int emin, int emax,
bool ibm_ld) bool ibm_ld)
@ -80,8 +100,11 @@ round_str (FILE *fout, const char *s, int prec, int emin, int emax,
mpfr_set_emin (emin); mpfr_set_emin (emin);
mpfr_set_emax (emax); mpfr_set_emax (emax);
mpfr_init (f); mpfr_init (f);
string_to_fp (f, s, MPFR_RNDZ);
bool underflow_before_rounding = mpfr_underflow_p () != 0;
int r = string_to_fp (f, s, MPFR_RNDD); int r = string_to_fp (f, s, MPFR_RNDD);
bool overflow = mpfr_overflow_p () != 0; bool overflow = mpfr_overflow_p () != 0;
bool underflow = mpfr_underflow_p () != 0;
if (ibm_ld) if (ibm_ld)
{ {
assert (prec == 106 && emin == -1073 && emax == 1024); assert (prec == 106 && emin == -1073 && emax == 1024);
@ -97,19 +120,27 @@ round_str (FILE *fout, const char *s, int prec, int emin, int emax,
} }
} }
mpfr_fprintf (fout, "\t%s,\n", r ? "false" : "true"); mpfr_fprintf (fout, "\t%s,\n", r ? "false" : "true");
print_fp (fout, f, overflow ? ", true,\n" : ", false,\n"); print_fp (fout, f,
suffix_to_print (overflow, underflow, underflow_before_rounding,
true));
string_to_fp (f, s, MPFR_RNDN); string_to_fp (f, s, MPFR_RNDN);
overflow = (mpfr_overflow_p () != 0 overflow = (mpfr_overflow_p () != 0
|| (ibm_ld && mpfr_cmpabs (f, max_value) > 0)); || (ibm_ld && mpfr_cmpabs (f, max_value) > 0));
print_fp (fout, f, overflow ? ", true,\n" : ", false,\n"); print_fp (fout, f,
suffix_to_print (overflow, underflow, underflow_before_rounding,
true));
string_to_fp (f, s, MPFR_RNDZ); string_to_fp (f, s, MPFR_RNDZ);
overflow = (mpfr_overflow_p () != 0 overflow = (mpfr_overflow_p () != 0
|| (ibm_ld && mpfr_cmpabs (f, max_value) > 0)); || (ibm_ld && mpfr_cmpabs (f, max_value) > 0));
print_fp (fout, f, overflow ? ", true,\n" : ", false,\n"); print_fp (fout, f,
suffix_to_print (overflow, underflow, underflow_before_rounding,
true));
string_to_fp (f, s, MPFR_RNDU); string_to_fp (f, s, MPFR_RNDU);
overflow = (mpfr_overflow_p () != 0 overflow = (mpfr_overflow_p () != 0
|| (ibm_ld && mpfr_cmpabs (f, max_value) > 0)); || (ibm_ld && mpfr_cmpabs (f, max_value) > 0));
print_fp (fout, f, overflow ? ", true" : ", false"); print_fp (fout, f,
suffix_to_print (overflow, underflow, underflow_before_rounding,
false));
mpfr_clear (f); mpfr_clear (f);
if (ibm_ld) if (ibm_ld)
mpfr_clear (max_value); mpfr_clear (max_value);

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <math-tests.h> #include <math-tests.h>
#include <tininess.h>
#include "tst-strtod.h" #include "tst-strtod.h"
@ -139,16 +140,26 @@
gen-tst-strtod-round utility to select the appropriately gen-tst-strtod-round utility to select the appropriately
rounded long double value for a given format. */ rounded long double value for a given format. */
#define TEST(s, \ #define TEST(s, \
fx, fd, fdo, fn, fno, fz, fzo, fu, fuo, \ fx, fd, fdo, fdu, fn, fno, fnu, \
dx, dd, ddo, dn, dno, dz, dzo, du, duo, \ fz, fzo, fzu, fu, fuo, fuu, \
ld64ix, ld64id, ld64ido, ld64in, ld64ino, \ dx, dd, ddo, ddu, dn, dno, dnu, \
ld64iz, ld64izo, ld64iu, ld64iuo, \ dz, dzo, dzu, du, duo, duu, \
ld64mx, ld64md, ld64mdo, ld64mn, ld64mno, \ ld64ix, ld64id, ld64ido, ld64idu, \
ld64mz, ld64mzo, ld64mu, ld64muo, \ ld64in, ld64ino, ld64inu, \
ld106x, ld106d, ld106do, ld106n, ld106no, \ ld64iz, ld64izo, ld64izu, \
ld106z, ld106zo, ld106u, ld106uo, \ ld64iu, ld64iuo, ld64iuu, \
ld113x, ld113d, ld113do, ld113n, ld113no, \ ld64mx, ld64md, ld64mdo, ld64mdu, \
ld113z, ld113zo, ld113u, ld113uo) \ ld64mn, ld64mno, ld64mnu, \
ld64mz, ld64mzo, ld64mzu, \
ld64mu, ld64muo, ld64muu, \
ld106x, ld106d, ld106do, ld106du, \
ld106n, ld106no, ld106nu, \
ld106z, ld106zo, ld106zu, \
ld106u, ld106uo, ld106uu, \
ld113x, ld113d, ld113do, ld113du, \
ld113n, ld113no, ld113nu, \
ld113z, ld113zo, ld113zu, \
ld113u, ld113uo, ld113uu) \
{ \ { \
L_ (s), \ L_ (s), \
{ XNTRY (fx, dx, ld64ix, ld64mx, ld106x, ld113x) }, \ { XNTRY (fx, dx, ld64ix, ld64mx, ld106x, ld113x) }, \
@ -163,6 +174,12 @@
{ XNTRY (fdo, ddo, ld64ido, ld64mdo, ld106do, ld113do) }, \ { XNTRY (fdo, ddo, ld64ido, ld64mdo, ld106do, ld113do) }, \
{ XNTRY (fzo, dzo, ld64izo, ld64mzo, ld106zo, ld113zo) }, \ { XNTRY (fzo, dzo, ld64izo, ld64mzo, ld106zo, ld113zo) }, \
{ XNTRY (fuo, duo, ld64iuo, ld64muo, ld106uo, ld113uo) } \ { XNTRY (fuo, duo, ld64iuo, ld64muo, ld106uo, ld113uo) } \
}, \
{ \
{ XNTRY (fnu, dnu, ld64inu, ld64mnu, ld106nu, ld113nu) }, \
{ XNTRY (fdu, ddu, ld64idu, ld64mdu, ld106du, ld113du) }, \
{ XNTRY (fzu, dzu, ld64izu, ld64mzu, ld106zu, ld113zu) }, \
{ XNTRY (fuu, duu, ld64iuu, ld64muu, ld106uu, ld113uu) } \
} \ } \
} }
@ -181,11 +198,17 @@ struct test_overflow
STRUCT_FOREACH_FLOAT_BOOL STRUCT_FOREACH_FLOAT_BOOL
}; };
struct test_underflow
{
STRUCT_FOREACH_FLOAT_BOOL
};
struct test { struct test {
const CHAR *s; const CHAR *s;
struct test_exactness exact; struct test_exactness exact;
struct test_results r[4]; struct test_results r[4];
struct test_overflow o[4]; struct test_overflow o[4];
struct test_underflow u[4];
}; };
/* Include the generated test data. */ /* Include the generated test data. */
@ -203,10 +226,14 @@ struct test {
# define FE_OVERFLOW 0 # define FE_OVERFLOW 0
#endif #endif
#ifndef FE_UNDERFLOW
# define FE_UNDERFLOW 0
#endif
#define GEN_ONE_TEST(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \ #define GEN_ONE_TEST(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \
{ \ { \
feclearexcept (FE_ALL_EXCEPT); \ feclearexcept (FE_ALL_EXCEPT); \
errno = 0; \ errno = 12345; \
FTYPE f = STRTO (FSUF) (s, NULL); \ FTYPE f = STRTO (FSUF) (s, NULL); \
int new_errno = errno; \ int new_errno = errno; \
if (f != expected->FSUF \ if (f != expected->FSUF \
@ -265,6 +292,40 @@ struct test {
s, new_errno, ERANGE); \ s, new_errno, ERANGE); \
result = 1; \ result = 1; \
} \ } \
if (FE_UNDERFLOW != 0) \
{ \
bool underflow_raised \
= fetestexcept (FE_UNDERFLOW) != 0; \
if (underflow_raised != underflow->FSUF) \
{ \
printf (FNPFXS "to" #FSUF \
" (" STRM ") underflow %d " \
"not %d\n", s, underflow_raised, \
underflow->FSUF); \
if (EXCEPTION_TESTS (FTYPE)) \
result = 1; \
else \
printf ("ignoring this exception error\n"); \
} \
} \
if (underflow->FSUF && new_errno != ERANGE) \
{ \
printf (FNPFXS "to" #FSUF \
" (" STRM ") left errno == %d," \
" not %d (ERANGE)\n", \
s, new_errno, ERANGE); \
result = 1; \
} \
if (!overflow->FSUF \
&& !underflow->FSUF \
&& new_errno != 12345) \
{ \
printf (FNPFXS "to" #FSUF \
" (" STRM ") set errno == %d," \
" should be unchanged\n", \
s, new_errno); \
result = 1; \
} \
} \ } \
} }
@ -272,6 +333,7 @@ static int
test_in_one_mode (const CHAR *s, const struct test_results *expected, test_in_one_mode (const CHAR *s, const struct test_results *expected,
const struct test_exactness *exact, const struct test_exactness *exact,
const struct test_overflow *overflow, const struct test_overflow *overflow,
const struct test_underflow *underflow,
const char *mode_name, int rnd_mode) const char *mode_name, int rnd_mode)
{ {
int result = 0; int result = 0;
@ -307,6 +369,7 @@ do_test (void)
{ {
result |= test_in_one_mode (tests[i].s, &tests[i].r[modes[0].rnd_i], result |= test_in_one_mode (tests[i].s, &tests[i].r[modes[0].rnd_i],
&tests[i].exact, &tests[i].o[modes[0].rnd_i], &tests[i].exact, &tests[i].o[modes[0].rnd_i],
&tests[i].u[modes[0].rnd_i],
modes[0].mode_name, modes[0].rnd_mode); modes[0].mode_name, modes[0].rnd_mode);
for (const struct fetestmodes *m = &modes[1]; m->mode_name != NULL; m++) for (const struct fetestmodes *m = &modes[1]; m->mode_name != NULL; m++)
{ {
@ -314,7 +377,9 @@ do_test (void)
{ {
result |= test_in_one_mode (tests[i].s, &tests[i].r[m->rnd_i], result |= test_in_one_mode (tests[i].s, &tests[i].r[m->rnd_i],
&tests[i].exact, &tests[i].exact,
&tests[i].o[m->rnd_i], m->mode_name, &tests[i].o[m->rnd_i],
&tests[i].u[m->rnd_i],
m->mode_name,
m->rnd_mode); m->rnd_mode);
fesetround (save_round_mode); fesetround (save_round_mode);
} }