mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-11-03 20:53:13 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			226 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Test for strtod handling of arguments that may cause floating-point
 | 
						|
   underflow.
 | 
						|
   Copyright (C) 2012-2013 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 = 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"
 |