Blame math/fromfp.h

Packit 6c4009
/* Round to integer type.  Common helper functions.
Packit 6c4009
   Copyright (C) 2016-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 <math.h>
Packit 6c4009
#include <math-barriers.h>
Packit 6c4009
#include <math_private.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
Packit 6c4009
/* The including file should have defined UNSIGNED to 0 (signed return
Packit 6c4009
   type) or 1 (unsigned return type), INEXACT to 0 (no inexact
Packit 6c4009
   exceptions) or 1 (raise inexact exceptions) and RET_TYPE to the
Packit 6c4009
   return type (intmax_t or uintmax_t).  */
Packit 6c4009
Packit 6c4009
/* Return the maximum unbiased exponent for an argument (negative if
Packit 6c4009
   NEGATIVE is set) that might be in range for a call to a fromfp
Packit 6c4009
   function with width WIDTH (greater than 0, and not exceeding that
Packit 6c4009
   of intmax_t).  The truncated argument may still be out of range in
Packit 6c4009
   the case of negative arguments, and if not out of range it may
Packit 6c4009
   become out of range as a result of rounding.  */
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
fromfp_max_exponent (bool negative, int width)
Packit 6c4009
{
Packit 6c4009
  if (UNSIGNED)
Packit 6c4009
    return negative ? -1 : width - 1;
Packit 6c4009
  else
Packit 6c4009
    return negative ? width - 1 : width - 2;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Return the result of rounding an integer value X (passed as the
Packit 6c4009
   absolute value; NEGATIVE is true if the value is negative), where
Packit 6c4009
   HALF_BIT is true if the bit with value 0.5 is set and MORE_BITS is
Packit 6c4009
   true if any lower bits are set, in the rounding direction
Packit 6c4009
   ROUND.  */
Packit 6c4009
Packit 6c4009
static uintmax_t
Packit 6c4009
fromfp_round (bool negative, uintmax_t x, bool half_bit, bool more_bits,
Packit 6c4009
	      int round)
Packit 6c4009
{
Packit 6c4009
  switch (round)
Packit 6c4009
    {
Packit 6c4009
    case FP_INT_UPWARD:
Packit 6c4009
      return x + (!negative && (half_bit || more_bits));
Packit 6c4009
Packit 6c4009
    case FP_INT_DOWNWARD:
Packit 6c4009
      return x + (negative && (half_bit || more_bits));
Packit 6c4009
Packit 6c4009
    case FP_INT_TOWARDZERO:
Packit 6c4009
    default:
Packit 6c4009
      /* Unknown rounding directions are defined to mean unspecified
Packit 6c4009
	 rounding; treat this as truncation.  */
Packit 6c4009
      return x;
Packit 6c4009
Packit 6c4009
    case FP_INT_TONEARESTFROMZERO:
Packit 6c4009
      return x + half_bit;
Packit 6c4009
Packit 6c4009
    case FP_INT_TONEAREST:
Packit 6c4009
      return x + (half_bit && ((x & 1) || more_bits));
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Integer rounding, of a value whose exponent EXPONENT did not exceed
Packit 6c4009
   the maximum exponent MAX_EXPONENT and so did not necessarily
Packit 6c4009
   overflow, has produced X (possibly wrapping to 0); the sign is
Packit 6c4009
   negative if NEGATIVE is true.  Return whether this overflowed the
Packit 6c4009
   allowed width.  */
Packit 6c4009
Packit 6c4009
static bool
Packit 6c4009
fromfp_overflowed (bool negative, uintmax_t x, int exponent, int max_exponent)
Packit 6c4009
{
Packit 6c4009
  if (UNSIGNED)
Packit 6c4009
    {
Packit 6c4009
      if (negative)
Packit 6c4009
	return x != 0;
Packit 6c4009
      else if (max_exponent == INTMAX_WIDTH - 1)
Packit 6c4009
	return exponent == INTMAX_WIDTH - 1 && x == 0;
Packit 6c4009
      else
Packit 6c4009
	return x == (1ULL << (max_exponent + 1));
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      if (negative)
Packit 6c4009
	return exponent == max_exponent && x != (1ULL << max_exponent);
Packit 6c4009
      else
Packit 6c4009
	return x == (1ULL << (max_exponent + 1));
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Handle a domain error for a call to a fromfp function with an
Packit 6c4009
   argument which is negative if NEGATIVE is set, and specified width
Packit 6c4009
   (not exceeding that of intmax_t) WIDTH.  The return value is
Packit 6c4009
   unspecified (with it being unclear if the result needs to fit
Packit 6c4009
   within WIDTH bits in this case); we choose to saturate to the given
Packit 6c4009
   number of bits (treating NaNs like any other value).  */
Packit 6c4009
Packit 6c4009
static RET_TYPE
Packit 6c4009
fromfp_domain_error (bool negative, unsigned int width)
Packit 6c4009
{
Packit 6c4009
  feraiseexcept (FE_INVALID);
Packit 6c4009
  __set_errno (EDOM);
Packit 6c4009
  /* The return value is unspecified; we choose to saturate to the
Packit 6c4009
     given number of bits (treating NaNs like any other value).  */
Packit 6c4009
  if (UNSIGNED)
Packit 6c4009
    {
Packit 6c4009
      if (negative)
Packit 6c4009
	return 0;
Packit 6c4009
      else if (width == INTMAX_WIDTH)
Packit 6c4009
	return -1;
Packit 6c4009
      else
Packit 6c4009
	return (1ULL << width) - 1;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      if (width == 0)
Packit 6c4009
	return 0;
Packit 6c4009
      else if (negative)
Packit 6c4009
	return -(1ULL << (width - 1));
Packit 6c4009
      else
Packit 6c4009
	return (1ULL << (width - 1)) - 1;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Given X, the absolute value of a floating-point number (negative if
Packit 6c4009
   NEGATIVE is set) truncated towards zero, where HALF_BIT is true if
Packit 6c4009
   the bit with value 0.5 is set and MORE_BITS is true if any lower
Packit 6c4009
   bits are set, round it in the rounding direction ROUND, handle
Packit 6c4009
   errors and exceptions and return the appropriate return value for a
Packit 6c4009
   fromfp function.  X originally had floating-point exponent
Packit 6c4009
   EXPONENT, which does not exceed MAX_EXPONENT, the return value from
Packit 6c4009
   fromfp_max_exponent with width WIDTH.  */
Packit 6c4009
Packit 6c4009
static RET_TYPE
Packit 6c4009
fromfp_round_and_return (bool negative, uintmax_t x, bool half_bit,
Packit 6c4009
			 bool more_bits, int round, int exponent,
Packit 6c4009
			 int max_exponent, unsigned int width)
Packit 6c4009
{
Packit 6c4009
  uintmax_t uret = fromfp_round (negative, x, half_bit, more_bits, round);
Packit 6c4009
  if (fromfp_overflowed (negative, uret, exponent, max_exponent))
Packit 6c4009
    return fromfp_domain_error (negative, width);
Packit 6c4009
Packit 6c4009
  if (INEXACT && (half_bit || more_bits))
Packit 6c4009
    {
Packit 6c4009
      /* There is no need for this to use the specific floating-point
Packit 6c4009
	 type for which this header is included, and there is no need
Packit 6c4009
	 for this header to know that type at all, so just use float
Packit 6c4009
	 here.  */
Packit 6c4009
      float force_inexact = 1.0f + FLT_MIN;
Packit 6c4009
      math_force_eval (force_inexact);
Packit 6c4009
    }
Packit 6c4009
  if (UNSIGNED)
Packit 6c4009
    /* A negative argument not rounding to zero will already have
Packit 6c4009
       produced a domain error.  */
Packit 6c4009
    return uret;
Packit 6c4009
  else
Packit 6c4009
    return negative ? -uret : uret;
Packit 6c4009
}