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

Packit 6c4009
/* s_frexpl.c -- long double version of s_frexp.c.
Packit 6c4009
 * Conversion to IEEE quad long double by Jakub Jelinek, jj@ultra.linux.cz.
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * ====================================================
Packit 6c4009
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
Packit 6c4009
 *
Packit 6c4009
 * Developed at SunPro, a Sun Microsystems, Inc. business.
Packit 6c4009
 * Permission to use, copy, modify, and distribute this
Packit 6c4009
 * software is freely granted, provided that this notice
Packit 6c4009
 * is preserved.
Packit 6c4009
 * ====================================================
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
#if defined(LIBM_SCCS) && !defined(lint)
Packit 6c4009
static char rcsid[] = "$NetBSD: $";
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * for non-zero x
Packit 6c4009
 *	x = frexpl(arg,&exp);
Packit 6c4009
 * return a long double fp quantity x such that 0.5 <= |x| <1.0
Packit 6c4009
 * and the corresponding binary exponent "exp". That is
Packit 6c4009
 *	arg = x*2^exp.
Packit 6c4009
 * If arg is inf, 0.0, or NaN, then frexpl(arg,&exp) returns arg
Packit 6c4009
 * with *exp=0.
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
#include <math.h>
Packit 6c4009
#include <math_private.h>
Packit 6c4009
#include <math_ldbl_opt.h>
Packit 6c4009
Packit 6c4009
long double __frexpl(long double x, int *eptr)
Packit 6c4009
{
Packit 6c4009
  uint64_t hx, lx, ix, ixl;
Packit 6c4009
  int64_t explo, expon;
Packit 6c4009
  double xhi, xlo;
Packit 6c4009
Packit 6c4009
  ldbl_unpack (x, &xhi, &xlo;;
Packit 6c4009
  EXTRACT_WORDS64 (hx, xhi);
Packit 6c4009
  EXTRACT_WORDS64 (lx, xlo);
Packit 6c4009
  ixl = 0x7fffffffffffffffULL & lx;
Packit 6c4009
  ix  = 0x7fffffffffffffffULL & hx;
Packit 6c4009
  expon = 0;
Packit 6c4009
  if (ix >= 0x7ff0000000000000ULL || ix == 0)
Packit 6c4009
    {
Packit 6c4009
      /* 0,inf,nan.  */
Packit 6c4009
      *eptr = expon;
Packit 6c4009
      return x + x;
Packit 6c4009
    }
Packit 6c4009
  expon = ix >> 52;
Packit 6c4009
  if (expon == 0)
Packit 6c4009
    {
Packit 6c4009
      /* Denormal high double, the low double must be 0.0.  */
Packit 6c4009
      int cnt;
Packit 6c4009
Packit 6c4009
      /* Normalize.  */
Packit 6c4009
      if (sizeof (ix) == sizeof (long))
Packit 6c4009
	cnt = __builtin_clzl (ix);
Packit 6c4009
      else if ((ix >> 32) != 0)
Packit 6c4009
	cnt = __builtin_clzl ((long) (ix >> 32));
Packit 6c4009
      else
Packit 6c4009
	cnt = __builtin_clzl ((long) ix) + 32;
Packit 6c4009
      cnt = cnt - 12;
Packit 6c4009
      expon -= cnt;
Packit 6c4009
      ix <<= cnt + 1;
Packit 6c4009
    }
Packit 6c4009
  expon -= 1022;
Packit 6c4009
  ix &= 0x000fffffffffffffULL;
Packit 6c4009
  hx &= 0x8000000000000000ULL;
Packit 6c4009
  hx |= (1022LL << 52) | ix;
Packit 6c4009
Packit 6c4009
  if (ixl != 0)
Packit 6c4009
    {
Packit 6c4009
      /* If the high double is an exact power of two and the low
Packit 6c4009
	 double has the opposite sign, then the exponent calculated
Packit 6c4009
	 from the high double is one too big.  */
Packit 6c4009
      if (ix == 0
Packit 6c4009
	  && (int64_t) (hx ^ lx) < 0)
Packit 6c4009
	{
Packit 6c4009
	  hx += 1LL << 52;
Packit 6c4009
	  expon -= 1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      explo = ixl >> 52;
Packit 6c4009
      if (explo == 0)
Packit 6c4009
	{
Packit 6c4009
	  /* The low double started out as a denormal.  Normalize its
Packit 6c4009
	     mantissa and adjust the exponent.  */
Packit 6c4009
	  int cnt;
Packit 6c4009
Packit 6c4009
	  if (sizeof (ixl) == sizeof (long))
Packit 6c4009
	    cnt = __builtin_clzl (ixl);
Packit 6c4009
	  else if ((ixl >> 32) != 0)
Packit 6c4009
	    cnt = __builtin_clzl ((long) (ixl >> 32));
Packit 6c4009
	  else
Packit 6c4009
	    cnt = __builtin_clzl ((long) ixl) + 32;
Packit 6c4009
	  cnt = cnt - 12;
Packit 6c4009
	  explo -= cnt;
Packit 6c4009
	  ixl <<= cnt + 1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* With variable precision we can't assume much about the
Packit 6c4009
	 magnitude of the returned low double.  It may even be a
Packit 6c4009
	 denormal.  */
Packit 6c4009
      explo -= expon;
Packit 6c4009
      ixl &= 0x000fffffffffffffULL;
Packit 6c4009
      lx  &= 0x8000000000000000ULL;
Packit 6c4009
      if (explo <= 0)
Packit 6c4009
	{
Packit 6c4009
	  /* Handle denormal low double.  */
Packit 6c4009
	  if (explo > -52)
Packit 6c4009
	    {
Packit 6c4009
	      ixl |= 1LL << 52;
Packit 6c4009
	      ixl >>= 1 - explo;
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      ixl = 0;
Packit 6c4009
	      lx = 0;
Packit 6c4009
	      if ((hx & 0x7ff0000000000000ULL) == (1023LL << 52))
Packit 6c4009
		{
Packit 6c4009
		  /* Oops, the adjustment we made above for values a
Packit 6c4009
		     little smaller than powers of two turned out to
Packit 6c4009
		     be wrong since the returned low double will be
Packit 6c4009
		     zero.  This can happen if the input was
Packit 6c4009
		     something weird like 0x1p1000 - 0x1p-1000.  */
Packit 6c4009
		  hx -= 1LL << 52;
Packit 6c4009
		  expon += 1;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  explo = 0;
Packit 6c4009
	}
Packit 6c4009
      lx |= (explo << 52) | ixl;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  INSERT_WORDS64 (xhi, hx);
Packit 6c4009
  INSERT_WORDS64 (xlo, lx);
Packit 6c4009
  x = ldbl_pack (xhi, xlo);
Packit 6c4009
  *eptr = expon;
Packit 6c4009
  return x;
Packit 6c4009
}
Packit 6c4009
#if IS_IN (libm)
Packit 6c4009
long_double_symbol (libm, __frexpl, frexpl);
Packit 6c4009
#else
Packit 6c4009
long_double_symbol (libc, __frexpl, frexpl);
Packit 6c4009
#endif