Blame stdlib/tst-strtod-underflow.c

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