/***********************************************************************
**  File:  fpmacros.c
**
**  Contains:  C source code for implementations of floating-point
**             functions which involve float format numbers, as
**             defined in header <fp.h>.  In particular, this file
**             contains implementations of functions
**              __fpclassify(d,f), __isnormal(d,f), __isfinite(d,f),
**             __isnan(d,f), and __signbit(d,f).  This file targets
**             PowerPC platforms.
**
**  Written by:   Robert A. Murley, Ali Sazegari
**
**  Copyright:   c 2001 by Apple Computer, Inc., all rights reserved
**
**  Change History (most recent first):
**
**     07 Jul 01   ram      First created from fpfloatfunc.c, fp.c,
**				classify.c and sign.c in MathLib v3 Mac OS9.
**
***********************************************************************/

#include     <features.h>
#include     <sys/types.h>
#include     <math.h>
#include     "fp_private.h"

#define SIGN_MASK 0x80000000
#define NSIGN_MASK 0x7fffffff
#define FEXP_MASK 0x7f800000
#define FFRAC_MASK 0x007fffff

/***********************************************************************
   int __fpclassifyf(float x) returns the classification code of the
   argument x, as defined in <fp.h>.

   Exceptions:  INVALID signaled if x is a signaling NaN; in this case,
                the FP_QNAN code is returned.

   Calls:  none
***********************************************************************/

libm_hidden_proto(__fpclassifyf)
int __fpclassifyf ( float x )
{
   unsigned int iexp;

   union {
      u_int32_t lval;
      float fval;
   } z;

   z.fval = x;
   iexp = z.lval & FEXP_MASK;                 /* isolate float exponent */

   if (iexp == FEXP_MASK) {                   /* NaN or INF case */
      if ((z.lval & 0x007fffff) == 0)
         return FP_INFINITE;
	return FP_NAN;
   }

   if (iexp != 0)                             /* normal float */
      return FP_NORMAL;

   if (x == 0.0)
      return FP_ZERO;             /* zero */
   else
      return FP_SUBNORMAL;        /* must be subnormal */
}
libm_hidden_def(__fpclassifyf)


/***********************************************************************
      Function __fpclassify,
      Implementation of classify of a double number for the PowerPC.

   Exceptions:  INVALID signaled if x is a signaling NaN; in this case,
                the FP_QNAN code is returned.

   Calls:  none
***********************************************************************/

libm_hidden_proto(__fpclassify)
int __fpclassify ( double arg )
{
	register unsigned int exponent;
      union
            {
            dHexParts hex;
            double dbl;
            } x;

	x.dbl = arg;

	exponent = x.hex.high & dExpMask;
	if ( exponent == dExpMask )
		{
		if ( ( ( x.hex.high & dHighMan ) | x.hex.low ) == 0 )
			return FP_INFINITE;
		else
            	return FP_NAN;
		}
	else if ( exponent != 0)
		return FP_NORMAL;
	else {
		if ( arg == 0.0 )
			return FP_ZERO;
		else
			return FP_SUBNORMAL;
		}
}
libm_hidden_def(__fpclassify)


/***********************************************************************
   int __isnormalf(float x) returns nonzero if and only if x is a
   normalized float number and zero otherwise.

   Exceptions:  INVALID is raised if x is a signaling NaN; in this case,
                zero is returned.

   Calls:  none
***********************************************************************/

int __isnormalf ( float x );
int __isnormalf ( float x )
{
   unsigned int iexp;
   union {
      u_int32_t lval;
      float fval;
   } z;

   z.fval = x;
   iexp = z.lval & FEXP_MASK;                 /* isolate float exponent */
   return ((iexp != FEXP_MASK) && (iexp != 0));
}


int __isnormal ( double x );
int __isnormal ( double x )
{
	return ( __fpclassify ( x ) == FP_NORMAL );
}


/***********************************************************************
   int __isfinitef(float x) returns nonzero if and only if x is a
   finite (normal, subnormal, or zero) float number and zero otherwise.

   Exceptions:  INVALID is raised if x is a signaling NaN; in this case,
                zero is returned.

   Calls:  none
***********************************************************************/

int __finitef ( float x )
{
   union {
      u_int32_t lval;
      float fval;
   } z;

   z.fval = x;
   return ((z.lval & FEXP_MASK) != FEXP_MASK);
}
strong_alias(__finitef,finitef)

#if 0 /* use __finite in s_finite.c */
int __finite ( double x )
{
	return ( __fpclassify ( x ) >= FP_ZERO );
}
strong_alias(__finite,finite)
#endif


/***********************************************************************
   int __signbitf(float x) returns nonzero if and only if the sign
   bit of x is set and zero otherwise.

   Exceptions:  INVALID is raised if x is a signaling NaN.

   Calls:  none
***********************************************************************/

libm_hidden_proto(__signbitf)
int __signbitf ( float x )
{
   union {
      u_int32_t lval;
      float fval;
   } z;

   z.fval = x;
   return ((z.lval & SIGN_MASK) != 0);
}
libm_hidden_def(__signbitf)


/***********************************************************************
      Function sign of a double.
      Implementation of sign bit for the PowerPC.

   Calls:  none
***********************************************************************/

libm_hidden_proto(__signbit)
int __signbit ( double arg )
{
      union
            {
            dHexParts hex;
            double dbl;
            } x;
      int sign;

      x.dbl = arg;
      sign = ( ( x.hex.high & dSgnMask ) == dSgnMask ) ? 1 : 0;
      return sign;
}
libm_hidden_def(__signbit)


/***********************************************************************
* int __isinff(float x) returns -1 if value represents  negative
*	infinity,  1  if value represents positive infinity,
*	and 0 otherwise.
*
* Calls:  __signbit
* +***********************************************************************/
int __isinff ( float x )
{
    int class = __fpclassifyf(x);
    if ( class == FP_INFINITE ) {
	return ( (__signbitf(x)) ? -1 : 1);
    }
    return 0;
}
strong_alias(__isinff,isinff)

int __isinf ( double x )
{
    int class = __fpclassify(x);
    if ( class == FP_INFINITE ) {
	return ( (__signbit(x)) ? -1 : 1);
    }
    return 0;
}
strong_alias(__isinf,isinf)

#if 0
int __isinfl ( long double x )
{
    int class = __fpclassify(x);
    if ( class == FP_INFINITE ) {
	return ( (__signbit(x)) ? -1 : 1);
    }
    return 0;
}
strong_alias(__isinfl,isinfl)
#endif

/***********************************************************************
   int __isnanf(float x) returns nonzero if and only if x is a
   NaN and zero otherwise.

   Exceptions:  INVALID is raised if x is a signaling NaN; in this case,
                nonzero is returned.

   Calls:  none
***********************************************************************/

int __isnanf ( float x )
{
   union {
      u_int32_t lval;
      float fval;
   } z;

   z.fval = x;
   return (((z.lval&FEXP_MASK) == FEXP_MASK) && ((z.lval&FFRAC_MASK) != 0));
}
strong_alias(__isnanf,isnanf)

libm_hidden_proto(__isnan)
int __isnan ( double x )
{
	int class = __fpclassify(x);
	return ( class == FP_NAN );
}
libm_hidden_def(__isnan)
strong_alias(__isnan,isnan)

#if 0
int __isnanl ( long double x )
{
	int class = __fpclassify(x);
	return ( class == FP_NAN );
}
strong_alias(__isnanl,isnanl)
#endif