_fixdfdi.c   [plain text]


/* APPLE LOCAL file 5316398 improved float/double -> int64 functions */
#include <stdint.h>

int64_t
__fixdfdi (double x)
{
  union { double d; uint64_t u; } u = {x};
  uint64_t fabsx = u.u & 0x7fffffffffffffffULL;
  uint32_t exp = fabsx >> 52;
  int64_t result = 0;

  /* for very large and reasonably small values, regular int converter
     works fine */
  if (exp >= 52U + 1023U)	/* if( |x| >= 0x1.0p52 || isnan( x ) ) */
    {
      /* early out for error cases |x| >= 0x1.0p63 || isnan(x) */
      if (exp >= 1023U + 63U)
	{
	  /* special case for x == -0x1.0p63 */
	  if (-0x1.0p63 == x)
	    return 0x8000000000000000ULL;

	  /* huge, Inf, NaN */
	  result = (int32_t) x;	/* grab sign bit */
	  result >>= 63;	/* splat it across value */
	  /* return either 0x8000000000000000 or 0x7fffffffffffffff
	     according to sign bit */
	  result ^= 0x7fffffffffffffffULL;

	  return result;
	}

      /* 0x1.0p52 <= |x| < 0x1.0p63 always integer, but too big. Chop
         off some of the top. */
      u.u &= 0xFFFFFFFF00000000ULL;	/* truncate off some low bits */
      x -= u.d;			/* get remainder */

      /* accumulate the high part into result */
      int32_t hi = u.d * 0x1.0p-32;
      result += (int64_t) hi << 32;
    }
  else
    {				/* |x| < 0x1.0p52 */

      /* early out for |x| < 0x1.0p31 -- use hardware 32-bit conversion */
      if (exp < 1023U + 31U)
	return (int64_t) ((int32_t) x);

      /* The integer result fits in the significand, but there may be
         some fractional bits. Value is too large to use 32-bit
         hardware.

         create a mask that covers the high 32-bit part of the number
         and the whole integer part. */
      uint64_t intMask = (int64_t) 0xFFF0000000000000LL >> (exp - 1023);

      /* extract the full integer (round to integer in round to zero
         rounding mode) */
      u.u &= intMask;

      /* find the fractional part */
      double fraction = x - u.d;

      /* save the integer part */
      x = u.d;

      /* set inexact as needed */
      result = (int32_t) fraction;	/* always 0 */
    }

  /* xi is < 2**53 now and integer. Convert to integer representation. */
  if (x < 0.0)
    {
      u.d = x - 0x1.0p52;
      result -= u.u & 0x000FFFFFFFFFFFFFULL;
    }
  else
    {
      u.d = x + 0x1.0p52;
      result += u.u & 0x000FFFFFFFFFFFFFULL;
    }

  return result;
}