hjl / source-git / glibc

Forked from source-git/glibc 3 years ago
Clone

Blame sysdeps/ieee754/ldbl-128ibm/s_rintl.c

Packit 6c4009
/* Round to int long double floating-point values.
Packit 6c4009
   IBM extended format long double version.
Packit 6c4009
   Copyright (C) 2006-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
/* This has been coded in assembler because GCC makes such a mess of it
Packit 6c4009
   when it's coded in C.  */
Packit 6c4009
Packit 6c4009
#include <math.h>
Packit 6c4009
#include <fenv.h>
Packit 6c4009
#include <math-barriers.h>
Packit 6c4009
#include <math_private.h>
Packit 6c4009
#include <math_ldbl_opt.h>
Packit 6c4009
#include <float.h>
Packit 6c4009
#include <ieee754.h>
Packit 6c4009
Packit 6c4009
#ifdef USE_AS_NEARBYINTL
Packit 6c4009
# define rintl nearbyintl
Packit 6c4009
# define __rintl __nearbyintl
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
long double
Packit 6c4009
__rintl (long double x)
Packit 6c4009
{
Packit 6c4009
  double xh, xl, hi, lo;
Packit 6c4009
Packit 6c4009
  ldbl_unpack (x, &xh, &xl);
Packit 6c4009
Packit 6c4009
  /* Return Inf, Nan, +/-0 unchanged.  */
Packit 6c4009
  if (__builtin_expect (xh != 0.0
Packit 6c4009
			&& __builtin_isless (__builtin_fabs (xh),
Packit 6c4009
					     __builtin_inf ()), 1))
Packit 6c4009
    {
Packit 6c4009
      double orig_xh;
Packit 6c4009
      int save_round = fegetround ();
Packit 6c4009
Packit 6c4009
      /* Long double arithmetic, including the canonicalisation below,
Packit 6c4009
	 only works in round-to-nearest mode.  */
Packit 6c4009
#ifdef USE_AS_NEARBYINTL
Packit 6c4009
      SET_RESTORE_ROUND_NOEX (FE_TONEAREST);
Packit 6c4009
#else
Packit 6c4009
      fesetround (FE_TONEAREST);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
      /* Convert the high double to integer.  */
Packit 6c4009
      orig_xh = xh;
Packit 6c4009
      hi = ldbl_nearbyint (xh);
Packit 6c4009
Packit 6c4009
      /* Subtract integral high part from the value.  If the low double
Packit 6c4009
	 happens to be exactly 0.5 or -0.5, you might think that this
Packit 6c4009
	 subtraction could result in an incorrect conversion.  For
Packit 6c4009
	 instance, subtracting an odd number would cause this function
Packit 6c4009
	 to round in the wrong direction.  However, if we have a
Packit 6c4009
	 canonical long double with the low double 0.5 or -0.5, then the
Packit 6c4009
	 high double must be even.  */
Packit 6c4009
      xh -= hi;
Packit 6c4009
      ldbl_canonicalize (&xh, &xl);
Packit 6c4009
Packit 6c4009
      /* Now convert the low double, adjusted for any remainder from the
Packit 6c4009
	 high double.  */
Packit 6c4009
      lo = ldbl_nearbyint (xh);
Packit 6c4009
Packit 6c4009
      xh -= lo;
Packit 6c4009
      ldbl_canonicalize (&xh, &xl);
Packit 6c4009
Packit 6c4009
      switch (save_round)
Packit 6c4009
	{
Packit 6c4009
	case FE_TONEAREST:
Packit 6c4009
	  if (xl > 0.0 && xh == 0.5)
Packit 6c4009
	    lo += 1.0;
Packit 6c4009
	  else if (xl < 0.0 && -xh == 0.5)
Packit 6c4009
	    lo -= 1.0;
Packit 6c4009
	  break;
Packit 6c4009
Packit 6c4009
	case FE_TOWARDZERO:
Packit 6c4009
	  if (orig_xh < 0.0)
Packit 6c4009
	    goto do_up;
Packit 6c4009
	  /* Fall thru */
Packit 6c4009
Packit 6c4009
	case FE_DOWNWARD:
Packit 6c4009
	  if (xh < 0.0 || (xh == 0.0 && xl < 0.0))
Packit 6c4009
	    lo -= 1.0;
Packit 6c4009
	  break;
Packit 6c4009
Packit 6c4009
	case FE_UPWARD:
Packit 6c4009
	do_up:
Packit 6c4009
	  if (xh > 0.0 || (xh == 0.0 && xl > 0.0))
Packit 6c4009
	    lo += 1.0;
Packit 6c4009
	  break;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Ensure the final value is canonical.  In certain cases,
Packit 6c4009
         rounding causes hi,lo calculated so far to be non-canonical.  */
Packit 6c4009
      xh = hi;
Packit 6c4009
      xl = lo;
Packit 6c4009
      ldbl_canonicalize (&xh, &xl);
Packit 6c4009
Packit 6c4009
      /* Ensure we return -0 rather than +0 when appropriate.  */
Packit 6c4009
      if (orig_xh < 0.0)
Packit 6c4009
	xh = -__builtin_fabs (xh);
Packit 6c4009
Packit 6c4009
#ifdef USE_AS_NEARBYINTL
Packit 6c4009
      math_force_eval (xh);
Packit 6c4009
      math_force_eval (xl);
Packit 6c4009
#else
Packit 6c4009
      fesetround (save_round);
Packit 6c4009
#endif
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    /* Quiet signaling NaN arguments.  */
Packit 6c4009
    xh += xh;
Packit 6c4009
Packit 6c4009
  return ldbl_pack (xh, xl);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
long_double_symbol (libm, __rintl, rintl);