123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- /* Test for strtod handling of arguments that may cause floating-point
- underflow.
- Copyright (C) 2012-2019 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, see
- <http://www.gnu.org/licenses/>. */
- #include <errno.h>
- #include <fenv.h>
- #include <float.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <tininess.h>
- enum underflow_case
- {
- /* Result is exact or outside the subnormal range. */
- UNDERFLOW_NONE,
- /* Result has magnitude at most half way between the largest
- subnormal value and the smallest positive normal value, and is
- not exact, so underflows in all rounding modes and independent
- of how tininess is detected. */
- UNDERFLOW_ALWAYS,
- /* Result is positive, with magnitude larger than half way between
- the largest subnormal value and the least positive normal
- value, but would underflow when rounded to nearest to normal
- precision, so underflows after rounding in all modes except
- rounding upward. */
- UNDERFLOW_EXCEPT_UPWARD,
- /* Likewise, for a negative result, underflowing after rounding
- except when rounding downward. */
- UNDERFLOW_EXCEPT_DOWNWARD,
- /* Result is positive, with magnitude at least three quarters of
- the way from the largest subnormal value to the smallest
- positive normal value, so underflows after rounding only when
- rounding downward or toward zero. */
- UNDERFLOW_ONLY_DOWNWARD_ZERO,
- /* Likewise, for a negative result, underflowing after rounding
- only when rounding upward or toward zero. */
- UNDERFLOW_ONLY_UPWARD_ZERO,
- };
- struct test
- {
- const char *s;
- 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 },
- };
- /* Return whether to expect underflow from a particular testcase, in a
- given rounding mode. */
- static bool
- expect_underflow (enum underflow_case c, int rm)
- {
- if (c == UNDERFLOW_NONE)
- return false;
- if (c == UNDERFLOW_ALWAYS)
- return true;
- if (TININESS_AFTER_ROUNDING)
- {
- switch (rm)
- {
- #ifdef FE_DOWNWARD
- case FE_DOWNWARD:
- return (c == UNDERFLOW_EXCEPT_UPWARD
- || c == UNDERFLOW_ONLY_DOWNWARD_ZERO);
- #endif
- #ifdef FE_TOWARDZERO
- case FE_TOWARDZERO:
- return true;
- #endif
- #ifdef FE_UPWARD
- case FE_UPWARD:
- return (c == UNDERFLOW_EXCEPT_DOWNWARD
- || c == UNDERFLOW_ONLY_UPWARD_ZERO);
- #endif
- default:
- return (c == UNDERFLOW_EXCEPT_UPWARD
- || c == UNDERFLOW_EXCEPT_DOWNWARD);
- }
- }
- else
- return true;
- }
- 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)
- {
- 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;
- #else
- bool got_fe_underflow = 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;
- }
- static int
- do_test (void)
- {
- int save_round_mode __attribute__ ((unused)) = fegetround ();
- int result = 0;
- #ifdef FE_TONEAREST
- const int fe_tonearest = FE_TONEAREST;
- #else
- const int fe_tonearest = 0;
- # if defined FE_DOWNWARD || defined FE_TOWARDZERO || defined FE_UPWARD
- # error "FE_TONEAREST not defined, but another rounding mode is"
- # endif
- #endif
- #ifdef FE_UNDERFLOW
- feclearexcept (FE_ALL_EXCEPT);
- dd = d * d;
- if (fetestexcept (FE_UNDERFLOW))
- support_underflow_exception = true;
- else
- puts ("underflow exception not supported at runtime, only testing errno");
- #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,
- "default rounding mode");
- #ifdef FE_DOWNWARD
- if (!fesetround (FE_DOWNWARD))
- {
- result |= test_in_one_mode (tests[i].s, tests[i].c, FE_DOWNWARD,
- "FE_DOWNWARD");
- fesetround (save_round_mode);
- }
- #endif
- #ifdef FE_TOWARDZERO
- if (!fesetround (FE_TOWARDZERO))
- {
- result |= test_in_one_mode (tests[i].s, tests[i].c, FE_TOWARDZERO,
- "FE_TOWARDZERO");
- fesetround (save_round_mode);
- }
- #endif
- #ifdef FE_UPWARD
- if (!fesetround (FE_UPWARD))
- {
- result |= test_in_one_mode (tests[i].s, tests[i].c, FE_UPWARD,
- "FE_UPWARD");
- fesetround (save_round_mode);
- }
- #endif
- }
- return result;
- }
- #define TEST_FUNCTION do_test ()
- #include "../test-skeleton.c"
|