Blame math/math-narrow.h

Packit 6c4009
/* Helper macros for functions returning a narrower type.
Packit 6c4009
   Copyright (C) 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
#ifndef	_MATH_NARROW_H
Packit 6c4009
#define	_MATH_NARROW_H	1
Packit 6c4009
Packit 6c4009
#include <bits/floatn.h>
Packit 6c4009
#include <bits/long-double.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fenv.h>
Packit 6c4009
#include <ieee754.h>
Packit 6c4009
#include <math-barriers.h>
Packit 6c4009
#include <math_private.h>
Packit 6c4009
Packit 6c4009
/* Carry out a computation using round-to-odd.  The computation is
Packit 6c4009
   EXPR; the union type in which to store the result is UNION and the
Packit 6c4009
   subfield of the "ieee" field of that union with the low part of the
Packit 6c4009
   mantissa is MANTISSA; SUFFIX is the suffix for the libc_fe* macros
Packit 6c4009
   to ensure that the correct rounding mode is used, for platforms
Packit 6c4009
   with multiple rounding modes where those macros set only the
Packit 6c4009
   relevant mode.  This macro does not work correctly if the sign of
Packit 6c4009
   an exact zero result depends on the rounding mode, so that case
Packit 6c4009
   must be checked for separately.  */
Packit 6c4009
#define ROUND_TO_ODD(EXPR, UNION, SUFFIX, MANTISSA)			\
Packit 6c4009
  ({									\
Packit 6c4009
    fenv_t env;								\
Packit 6c4009
    UNION u;								\
Packit 6c4009
									\
Packit 6c4009
    libc_feholdexcept_setround ## SUFFIX (&env, FE_TOWARDZERO);		\
Packit 6c4009
    u.d = (EXPR);							\
Packit 6c4009
    math_force_eval (u.d);						\
Packit 6c4009
    u.ieee.MANTISSA							\
Packit 6c4009
      |= libc_feupdateenv_test ## SUFFIX (&env, FE_INEXACT) != 0;	\
Packit 6c4009
									\
Packit 6c4009
    u.d;								\
Packit 6c4009
  })
Packit 6c4009
Packit 6c4009
/* Check for error conditions from a narrowing add function returning
Packit 6c4009
   RET with arguments X and Y and set errno as needed.  Overflow and
Packit 6c4009
   underflow can occur for finite arguments and a domain error for
Packit 6c4009
   infinite ones.  */
Packit 6c4009
#define CHECK_NARROW_ADD(RET, X, Y)			\
Packit 6c4009
  do							\
Packit 6c4009
    {							\
Packit 6c4009
      if (!isfinite (RET))				\
Packit 6c4009
	{						\
Packit 6c4009
	  if (isnan (RET))				\
Packit 6c4009
	    {						\
Packit 6c4009
	      if (!isnan (X) && !isnan (Y))		\
Packit 6c4009
		__set_errno (EDOM);			\
Packit 6c4009
	    }						\
Packit 6c4009
	  else if (isfinite (X) && isfinite (Y))	\
Packit 6c4009
	    __set_errno (ERANGE);			\
Packit 6c4009
	}						\
Packit 6c4009
      else if ((RET) == 0 && (X) != -(Y))		\
Packit 6c4009
	__set_errno (ERANGE);				\
Packit 6c4009
    }							\
Packit 6c4009
  while (0)
Packit 6c4009
Packit 6c4009
/* Implement narrowing add using round-to-odd.  The arguments are X
Packit 6c4009
   and Y, the return type is TYPE and UNION, MANTISSA and SUFFIX are
Packit 6c4009
   as for ROUND_TO_ODD.  */
Packit 6c4009
#define NARROW_ADD_ROUND_TO_ODD(X, Y, TYPE, UNION, SUFFIX, MANTISSA)	\
Packit 6c4009
  do									\
Packit 6c4009
    {									\
Packit 6c4009
      TYPE ret;								\
Packit 6c4009
									\
Packit 6c4009
      /* Ensure a zero result is computed in the original rounding	\
Packit 6c4009
	 mode.  */							\
Packit 6c4009
      if ((X) == -(Y))							\
Packit 6c4009
	ret = (TYPE) ((X) + (Y));					\
Packit 6c4009
      else								\
Packit 6c4009
	ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) + (Y),		\
Packit 6c4009
				   UNION, SUFFIX, MANTISSA);		\
Packit 6c4009
									\
Packit 6c4009
      CHECK_NARROW_ADD (ret, (X), (Y));					\
Packit 6c4009
      return ret;							\
Packit 6c4009
    }									\
Packit 6c4009
  while (0)
Packit 6c4009
Packit 6c4009
/* Implement a narrowing add function that is not actually narrowing
Packit 6c4009
   or where no attempt is made to be correctly rounding (the latter
Packit 6c4009
   only applies to IBM long double).  The arguments are X and Y and
Packit 6c4009
   the return type is TYPE.  */
Packit 6c4009
#define NARROW_ADD_TRIVIAL(X, Y, TYPE)		\
Packit 6c4009
  do						\
Packit 6c4009
    {						\
Packit 6c4009
      TYPE ret;					\
Packit 6c4009
						\
Packit 6c4009
      ret = (TYPE) ((X) + (Y));			\
Packit 6c4009
      CHECK_NARROW_ADD (ret, (X), (Y));		\
Packit 6c4009
      return ret;				\
Packit 6c4009
    }						\
Packit 6c4009
  while (0)
Packit 6c4009
Packit 6c4009
/* Check for error conditions from a narrowing subtract function
Packit 6c4009
   returning RET with arguments X and Y and set errno as needed.
Packit 6c4009
   Overflow and underflow can occur for finite arguments and a domain
Packit 6c4009
   error for infinite ones.  */
Packit 6c4009
#define CHECK_NARROW_SUB(RET, X, Y)			\
Packit 6c4009
  do							\
Packit 6c4009
    {							\
Packit 6c4009
      if (!isfinite (RET))				\
Packit 6c4009
	{						\
Packit 6c4009
	  if (isnan (RET))				\
Packit 6c4009
	    {						\
Packit 6c4009
	      if (!isnan (X) && !isnan (Y))		\
Packit 6c4009
		__set_errno (EDOM);			\
Packit 6c4009
	    }						\
Packit 6c4009
	  else if (isfinite (X) && isfinite (Y))	\
Packit 6c4009
	    __set_errno (ERANGE);			\
Packit 6c4009
	}						\
Packit 6c4009
      else if ((RET) == 0 && (X) != (Y))		\
Packit 6c4009
	__set_errno (ERANGE);				\
Packit 6c4009
    }							\
Packit 6c4009
  while (0)
Packit 6c4009
Packit 6c4009
/* Implement narrowing subtract using round-to-odd.  The arguments are
Packit 6c4009
   X and Y, the return type is TYPE and UNION, MANTISSA and SUFFIX are
Packit 6c4009
   as for ROUND_TO_ODD.  */
Packit 6c4009
#define NARROW_SUB_ROUND_TO_ODD(X, Y, TYPE, UNION, SUFFIX, MANTISSA)	\
Packit 6c4009
  do									\
Packit 6c4009
    {									\
Packit 6c4009
      TYPE ret;								\
Packit 6c4009
									\
Packit 6c4009
      /* Ensure a zero result is computed in the original rounding	\
Packit 6c4009
	 mode.  */							\
Packit 6c4009
      if ((X) == (Y))							\
Packit 6c4009
	ret = (TYPE) ((X) - (Y));					\
Packit 6c4009
      else								\
Packit 6c4009
	ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) - (Y),		\
Packit 6c4009
				   UNION, SUFFIX, MANTISSA);		\
Packit 6c4009
									\
Packit 6c4009
      CHECK_NARROW_SUB (ret, (X), (Y));					\
Packit 6c4009
      return ret;							\
Packit 6c4009
    }									\
Packit 6c4009
  while (0)
Packit 6c4009
Packit 6c4009
/* Implement a narrowing subtract function that is not actually
Packit 6c4009
   narrowing or where no attempt is made to be correctly rounding (the
Packit 6c4009
   latter only applies to IBM long double).  The arguments are X and Y
Packit 6c4009
   and the return type is TYPE.  */
Packit 6c4009
#define NARROW_SUB_TRIVIAL(X, Y, TYPE)		\
Packit 6c4009
  do						\
Packit 6c4009
    {						\
Packit 6c4009
      TYPE ret;					\
Packit 6c4009
						\
Packit 6c4009
      ret = (TYPE) ((X) - (Y));			\
Packit 6c4009
      CHECK_NARROW_SUB (ret, (X), (Y));		\
Packit 6c4009
      return ret;				\
Packit 6c4009
    }						\
Packit 6c4009
  while (0)
Packit 6c4009
Packit 6c4009
/* Check for error conditions from a narrowing multiply function
Packit 6c4009
   returning RET with arguments X and Y and set errno as needed.
Packit 6c4009
   Overflow and underflow can occur for finite arguments and a domain
Packit 6c4009
   error for Inf * 0.  */
Packit 6c4009
#define CHECK_NARROW_MUL(RET, X, Y)			\
Packit 6c4009
  do							\
Packit 6c4009
    {							\
Packit 6c4009
      if (!isfinite (RET))				\
Packit 6c4009
	{						\
Packit 6c4009
	  if (isnan (RET))				\
Packit 6c4009
	    {						\
Packit 6c4009
	      if (!isnan (X) && !isnan (Y))		\
Packit 6c4009
		__set_errno (EDOM);			\
Packit 6c4009
	    }						\
Packit 6c4009
	  else if (isfinite (X) && isfinite (Y))	\
Packit 6c4009
	    __set_errno (ERANGE);			\
Packit 6c4009
	}						\
Packit 6c4009
      else if ((RET) == 0 && (X) != 0 && (Y) != 0)	\
Packit 6c4009
	__set_errno (ERANGE);				\
Packit 6c4009
    }							\
Packit 6c4009
  while (0)
Packit 6c4009
Packit 6c4009
/* Implement narrowing multiply using round-to-odd.  The arguments are
Packit 6c4009
   X and Y, the return type is TYPE and UNION, MANTISSA and SUFFIX are
Packit 6c4009
   as for ROUND_TO_ODD.  */
Packit 6c4009
#define NARROW_MUL_ROUND_TO_ODD(X, Y, TYPE, UNION, SUFFIX, MANTISSA)	\
Packit 6c4009
  do									\
Packit 6c4009
    {									\
Packit 6c4009
      TYPE ret;								\
Packit 6c4009
									\
Packit 6c4009
      ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) * (Y),		\
Packit 6c4009
				 UNION, SUFFIX, MANTISSA);		\
Packit 6c4009
									\
Packit 6c4009
      CHECK_NARROW_MUL (ret, (X), (Y));					\
Packit 6c4009
      return ret;							\
Packit 6c4009
    }									\
Packit 6c4009
  while (0)
Packit 6c4009
Packit 6c4009
/* Implement a narrowing multiply function that is not actually
Packit 6c4009
   narrowing or where no attempt is made to be correctly rounding (the
Packit 6c4009
   latter only applies to IBM long double).  The arguments are X and Y
Packit 6c4009
   and the return type is TYPE.  */
Packit 6c4009
#define NARROW_MUL_TRIVIAL(X, Y, TYPE)		\
Packit 6c4009
  do						\
Packit 6c4009
    {						\
Packit 6c4009
      TYPE ret;					\
Packit 6c4009
						\
Packit 6c4009
      ret = (TYPE) ((X) * (Y));			\
Packit 6c4009
      CHECK_NARROW_MUL (ret, (X), (Y));		\
Packit 6c4009
      return ret;				\
Packit 6c4009
    }						\
Packit 6c4009
  while (0)
Packit 6c4009
Packit 6c4009
/* Check for error conditions from a narrowing divide function
Packit 6c4009
   returning RET with arguments X and Y and set errno as needed.
Packit 6c4009
   Overflow, underflow and divide-by-zero can occur for finite
Packit 6c4009
   arguments and a domain error for Inf / Inf and 0 / 0.  */
Packit 6c4009
#define CHECK_NARROW_DIV(RET, X, Y)			\
Packit 6c4009
  do							\
Packit 6c4009
    {							\
Packit 6c4009
      if (!isfinite (RET))				\
Packit 6c4009
	{						\
Packit 6c4009
	  if (isnan (RET))				\
Packit 6c4009
	    {						\
Packit 6c4009
	      if (!isnan (X) && !isnan (Y))		\
Packit 6c4009
		__set_errno (EDOM);			\
Packit 6c4009
	    }						\
Packit 6c4009
	  else if (isfinite (X))			\
Packit 6c4009
	    __set_errno (ERANGE);			\
Packit 6c4009
	}						\
Packit 6c4009
      else if ((RET) == 0 && (X) != 0 && !isinf (Y))	\
Packit 6c4009
	__set_errno (ERANGE);				\
Packit 6c4009
    }							\
Packit 6c4009
  while (0)
Packit 6c4009
Packit 6c4009
/* Implement narrowing divide using round-to-odd.  The arguments are
Packit 6c4009
   X and Y, the return type is TYPE and UNION, MANTISSA and SUFFIX are
Packit 6c4009
   as for ROUND_TO_ODD.  */
Packit 6c4009
#define NARROW_DIV_ROUND_TO_ODD(X, Y, TYPE, UNION, SUFFIX, MANTISSA)	\
Packit 6c4009
  do									\
Packit 6c4009
    {									\
Packit 6c4009
      TYPE ret;								\
Packit 6c4009
									\
Packit 6c4009
      ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) / (Y),		\
Packit 6c4009
				 UNION, SUFFIX, MANTISSA);		\
Packit 6c4009
									\
Packit 6c4009
      CHECK_NARROW_DIV (ret, (X), (Y));					\
Packit 6c4009
      return ret;							\
Packit 6c4009
    }									\
Packit 6c4009
  while (0)
Packit 6c4009
Packit 6c4009
/* Implement a narrowing divide function that is not actually
Packit 6c4009
   narrowing or where no attempt is made to be correctly rounding (the
Packit 6c4009
   latter only applies to IBM long double).  The arguments are X and Y
Packit 6c4009
   and the return type is TYPE.  */
Packit 6c4009
#define NARROW_DIV_TRIVIAL(X, Y, TYPE)		\
Packit 6c4009
  do						\
Packit 6c4009
    {						\
Packit 6c4009
      TYPE ret;					\
Packit 6c4009
						\
Packit 6c4009
      ret = (TYPE) ((X) / (Y));			\
Packit 6c4009
      CHECK_NARROW_DIV (ret, (X), (Y));		\
Packit 6c4009
      return ret;				\
Packit 6c4009
    }						\
Packit 6c4009
  while (0)
Packit 6c4009
Packit 6c4009
/* The following macros declare aliases for a narrowing function.  The
Packit 6c4009
   sole argument is the base name of a family of functions, such as
Packit 6c4009
   "add".  If any platform changes long double format after the
Packit 6c4009
   introduction of narrowing functions, in a way requiring symbol
Packit 6c4009
   versioning compatibility, additional variants of these macros will
Packit 6c4009
   be needed.  */
Packit 6c4009
Packit 6c4009
#define libm_alias_float_double_main(func)	\
Packit 6c4009
  weak_alias (__f ## func, f ## func)		\
Packit 6c4009
  weak_alias (__f ## func, f32 ## func ## f64)	\
Packit 6c4009
  weak_alias (__f ## func, f32 ## func ## f32x)
Packit 6c4009
Packit 6c4009
#ifdef NO_LONG_DOUBLE
Packit 6c4009
# define libm_alias_float_double(func)		\
Packit 6c4009
  libm_alias_float_double_main (func)		\
Packit 6c4009
  weak_alias (__f ## func, f ## func ## l)
Packit 6c4009
#else
Packit 6c4009
# define libm_alias_float_double(func)		\
Packit 6c4009
  libm_alias_float_double_main (func)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#define libm_alias_float32x_float64_main(func)			\
Packit 6c4009
  weak_alias (__f32x ## func ## f64, f32x ## func ## f64)
Packit 6c4009
Packit 6c4009
#ifdef NO_LONG_DOUBLE
Packit 6c4009
# define libm_alias_float32x_float64(func)		\
Packit 6c4009
  libm_alias_float32x_float64_main (func)		\
Packit 6c4009
  weak_alias (__f32x ## func ## f64, d ## func ## l)
Packit 6c4009
#elif defined __LONG_DOUBLE_MATH_OPTIONAL
Packit 6c4009
# define libm_alias_float32x_float64(func)			\
Packit 6c4009
  libm_alias_float32x_float64_main (func)			\
Packit 6c4009
  weak_alias (__f32x ## func ## f64, __nldbl_d ## func ## l)
Packit 6c4009
#else
Packit 6c4009
# define libm_alias_float32x_float64(func)	\
Packit 6c4009
  libm_alias_float32x_float64_main (func)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if __HAVE_FLOAT128 && !__HAVE_DISTINCT_FLOAT128
Packit 6c4009
# define libm_alias_float_ldouble_f128(func)		\
Packit 6c4009
  weak_alias (__f ## func ## l, f32 ## func ## f128)
Packit 6c4009
# define libm_alias_double_ldouble_f128(func)		\
Packit 6c4009
  weak_alias (__d ## func ## l, f32x ## func ## f128)	\
Packit 6c4009
  weak_alias (__d ## func ## l, f64 ## func ## f128)
Packit 6c4009
#else
Packit 6c4009
# define libm_alias_float_ldouble_f128(func)
Packit 6c4009
# define libm_alias_double_ldouble_f128(func)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if __HAVE_FLOAT64X_LONG_DOUBLE
Packit 6c4009
# define libm_alias_float_ldouble_f64x(func)		\
Packit 6c4009
  weak_alias (__f ## func ## l, f32 ## func ## f64x)
Packit 6c4009
# define libm_alias_double_ldouble_f64x(func)		\
Packit 6c4009
  weak_alias (__d ## func ## l, f32x ## func ## f64x)	\
Packit 6c4009
  weak_alias (__d ## func ## l, f64 ## func ## f64x)
Packit 6c4009
#else
Packit 6c4009
# define libm_alias_float_ldouble_f64x(func)
Packit 6c4009
# define libm_alias_double_ldouble_f64x(func)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#define libm_alias_float_ldouble(func)		\
Packit 6c4009
  weak_alias (__f ## func ## l, f ## func ## l) \
Packit 6c4009
  libm_alias_float_ldouble_f128 (func)		\
Packit 6c4009
  libm_alias_float_ldouble_f64x (func)
Packit 6c4009
Packit 6c4009
#define libm_alias_double_ldouble(func)		\
Packit 6c4009
  weak_alias (__d ## func ## l, d ## func ## l) \
Packit 6c4009
  libm_alias_double_ldouble_f128 (func)		\
Packit 6c4009
  libm_alias_double_ldouble_f64x (func)
Packit 6c4009
Packit 6c4009
#define libm_alias_float64x_float128(func)			\
Packit 6c4009
  weak_alias (__f64x ## func ## f128, f64x ## func ## f128)
Packit 6c4009
Packit 6c4009
#define libm_alias_float32_float128_main(func)			\
Packit 6c4009
  weak_alias (__f32 ## func ## f128, f32 ## func ## f128)
Packit 6c4009
Packit 6c4009
#define libm_alias_float64_float128_main(func)			\
Packit 6c4009
  weak_alias (__f64 ## func ## f128, f64 ## func ## f128)	\
Packit 6c4009
  weak_alias (__f64 ## func ## f128, f32x ## func ## f128)
Packit 6c4009
Packit 6c4009
#if __HAVE_FLOAT64X_LONG_DOUBLE
Packit 6c4009
# define libm_alias_float32_float128(func)	\
Packit 6c4009
  libm_alias_float32_float128_main (func)
Packit 6c4009
# define libm_alias_float64_float128(func)	\
Packit 6c4009
  libm_alias_float64_float128_main (func)
Packit 6c4009
#else
Packit 6c4009
# define libm_alias_float32_float128(func)			\
Packit 6c4009
  libm_alias_float32_float128_main (func)			\
Packit 6c4009
  weak_alias (__f32 ## func ## f128, f32 ## func ## f64x)
Packit 6c4009
# define libm_alias_float64_float128(func)			\
Packit 6c4009
  libm_alias_float64_float128_main (func)			\
Packit 6c4009
  weak_alias (__f64 ## func ## f128, f64 ## func ## f64x)	\
Packit 6c4009
  weak_alias (__f64 ## func ## f128, f32x ## func ## f64x)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#endif /* math-narrow.h.  */