/*
 * Copyright (c) 2003-2012
 * Distributed Systems Software.  All rights reserved.
 * See the file LICENSE for redistribution information.
 */

/*
 * Some minor changes have been made to the original code for portability and
 * consistency reasons.
 */
#include "local.h"

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2012\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: trunc.c 2594 2012-10-19 17:28:49Z brachman $";
#endif

#ifndef HAVE_TRUNC
/* @(#)s_floor.c 5.1 93/09/24 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunPro, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice
 * is preserved.
 * ====================================================
 */

/* $FreeBSD: src/lib/msun/src/s_trunc.c,v 1.1 2004/06/20 09:25:43 das Exp $ */

/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunPro, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice
 * is preserved.
 * ====================================================
 */

/*
 * from: @(#)fdlibm.h 5.1 93/09/24
 * $FreeBSD: src/lib/msun/src/math_private.h,v 1.17 2005/02/04 20:05:39 das Exp $
 */

#include <sys/types.h>

#ifndef u_int32_t
#define u_int32_t		uint32_t
#endif

/*
 * The original fdlibm code used statements like:
 *	n0 = ((*(int*)&one)>>29)^1;		* index of high word *
 *	ix0 = *(n0+(int*)&x);			* high word of x *
 *	ix1 = *((1-n0)+(int*)&x);		* low word of x *
 * to dig two 32 bit words out of the 64 bit IEEE floating point
 * value.  That is non-ANSI, and, moreover, the gcc instruction
 * scheduler gets it wrong.  We instead use the following macros.
 * Unlike the original code, we determine the endianness at compile
 * time, not at run time; I don't see much benefit to selecting
 * endianness at run time.
 */
/*
 * A union which permits us to convert between a double and two 32 bit
 * ints.
 */

#ifdef DACS_OS_IS_BIG_ENDIAN
typedef union {
  double value;
  struct {
    u_int32_t msw;
    u_int32_t lsw;
  } parts;
} ieee_double_shape_type;
#endif

#ifdef DACS_OS_IS_LITTLE_ENDIAN
typedef union {
  double value;
  struct {
    u_int32_t lsw;
    u_int32_t msw;
  } parts;
} ieee_double_shape_type;
#endif

/* Get two 32 bit ints from a double.  */
#define EXTRACT_WORDS(ix0, ix1, d)	\
do {								\
  ieee_double_shape_type ew_u;		\
  ew_u.value = (d);					\
  (ix0) = ew_u.parts.msw;			\
  (ix1) = ew_u.parts.lsw;			\
} while (0)

/* Set a double from two 32 bit ints.  */
#define INSERT_WORDS(d, ix0, ix1)		\
do {								\
  ieee_double_shape_type iw_u;		\
  iw_u.parts.msw = (ix0);			\
  iw_u.parts.lsw = (ix1);			\
  (d) = iw_u.value;					\
} while (0)

static const double huge = 1.0e300;

/*
 * Return x rounded toward 0 to integral value
 * Method:
 *	Bit twiddling.
 * Exception:
 *	Inexact flag raised if x not equal to trunc(x).
 */
double
trunc(double x)
{
  int32_t i0, i1, j0;
  u_int32_t i, j;

  EXTRACT_WORDS(i0, i1, x);
  j0 = ((i0 >> 20) & 0x7ff) - 0x3ff;
  if (j0 < 20) {
	if (j0 < 0) { 	/* raise inexact if x != 0 */
	  if (huge + x > 0.0) {/* |x|<1, so return 0*sign(x) */
		i0 &= 0x80000000U;
		i1 = 0;
	  }
	} else {
	  i = (0x000fffff) >> j0;
	  if (((i0 & i) | i1) == 0)
		return(x); /* x is integral */
	  if (huge + x > 0.0) {	/* raise inexact flag */
		i0 &= (~i);
		i1=0;
	  }
	}
  } else if (j0 > 51) {
	if ( j0 == 0x400)
	  return(x + x);	/* inf or NaN */
	else
	  return(x);		/* x is integral */
  } else {
	i = ((u_int32_t) (0xffffffff)) >> (j0 - 20);
	if ((i1 & i) == 0)
	  return(x);	/* x is integral */
	if (huge + x > 0.0)		/* raise inexact flag */
	  i1 &= (~i);
  }
  INSERT_WORDS(x, i0, i1);
  return(x);
}
#endif
