/***
* intrncvt.c - internal floating point conversions
*
*	Copyright (c) 1992-2001, Microsoft Corporation.	All rights reserved.
*
*Purpose:
*   All fp string conversion routines use the same core conversion code
*   that converts strings into an internal long double representation
*   with an 80-bit mantissa field. The mantissa is represented
*   as an array (man) of 32-bit unsigned longs, with man[0] holding
*   the high order 32 bits of the mantissa. The binary point is assumed
*   to be between the MSB and MSB-1 of man[0].
*
*   Bits are counted as follows:
*
*
*     +-- binary point
*     |
*     v 		 MSB	       LSB
*   ----------------	 ------------------	 --------------------
*   |0 1    .... 31|	 | 32 33 ...	63|	 | 64 65 ...	  95|
*   ----------------	 ------------------	 --------------------
*
*   man[0]		    man[1]		     man[2]
*
*   This file provides the final conversion routines from this internal
*   form to the single, double, or long double precision floating point
*   format.
*
*   All these functions do not handle NaNs (it is not necessary)
*
*
*Revision History:
*   04-29-92	GDP	written
*   06-18-92	GDP	now ld12told returns INTRNCVT_STATUS
*   06-22-92	GDP	use new __strgtold12 interface (FORTRAN support)
*   10-25-92	GDP	_atoldbl bug fix (cuda 1345): if the mantissa overflows
*			set its MSB to 1)
*   06-08-98	JWM	fixed long-standing off-by-1 error in _RoundMan().
*
*******************************************************************************/


#include <cv.h>


#define INTRNMAN_LEN  3	      /* internal mantissa length in int's */

//
//  internal mantissaa representation
//  for string conversion routines
//

typedef u_long *intrnman;


typedef struct {
   int max_exp;      // maximum base 2 exponent (reserved for special values)
   int min_exp;      // minimum base 2 exponent (reserved for denormals)
   int precision;    // bits of precision carried in the mantissa
   int exp_width;    // number of bits for exponent
   int format_width; // format width in bits
   int bias;	     // exponent bias
} FpFormatDescriptor;



static FpFormatDescriptor
DoubleFormat = {
    0x7ff - 0x3ff,  //	1024, maximum base 2 exponent (reserved for special values)
    0x0   - 0x3ff,  // -1023, minimum base 2 exponent (reserved for denormals)
    53, 	    // bits of precision carried in the mantissa
    11, 	    // number of bits for exponent
    64, 	    // format width in bits
    0x3ff,	    // exponent bias
};

static FpFormatDescriptor
FloatFormat = {
    0xff - 0x7f,    //	128, maximum base 2 exponent (reserved for special values)
    0x0  - 0x7f,    // -127, minimum base 2 exponent (reserved for denormals)
    24, 	    // bits of precision carried in the mantissa
    8,		    // number of bits for exponent
    32, 	    // format width in bits
    0x7f,	    // exponent bias
};



//
// function prototypes
//

int _RoundMan (intrnman man, int nbit);
int _ZeroTail (intrnman man, int nbit);
int _IncMan (intrnman man, int nbit);
void _CopyMan (intrnman dest, intrnman src);
void _CopyMan (intrnman dest, intrnman src);
void _FillZeroMan(intrnman man);
void _Shrman (intrnman man, int n);

INTRNCVT_STATUS _ld12cvt(_LDBL12 *pld12, void *d, FpFormatDescriptor *format);

/***
* _ZeroTail - check if a mantissa ends in 0's
*
*Purpose:
*   Return TRUE if all mantissa bits after nbit (including nbit) are 0,
*   otherwise return FALSE
*
*
*Entry:
*   man: mantissa
*   nbit: order of bit where the tail begins
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/
int _ZeroTail (intrnman man, int nbit)
{
    int nl = nbit / 32;
    int nb = 31 - nbit % 32;


    //
    //		     |<---- tail to be checked --->
    //
    //	--  ------------------------	       ----
    //	|...	|		  |  ...	  |
    //	--  ------------------------	       ----
    //	^	^    ^
    //	|	|    |<----nb----->
    //	man	nl   nbit
    //



    u_long bitmask = ~(MAX_ULONG << nb);

    if (man[nl] & bitmask)
	return 0;

    nl++;

    for (;nl < INTRNMAN_LEN; nl++)
	if (man[nl])
	    return 0;

    return 1;
}




/***
* _IncMan - increment mantissa
*
*Purpose:
*
*
*Entry:
*   man: mantissa in internal long form
*   nbit: order of bit that specifies the end of the part to be incremented
*
*Exit:
*   returns 1 on overflow, 0 otherwise
*
*Exceptions:
*
*******************************************************************************/

int _IncMan (intrnman man, int nbit)
{
    int nl = nbit / 32;
    int nb = 31 - nbit % 32;

    //
    //	|<--- part to be incremented -->|
    //
    //	--	       ---------------------------     ----
    //	|...		  |			|   ...	  |
    //	--	       ---------------------------     ----
    //	^		  ^		^
    //	|		  |		|<--nb-->
    //	man		  nl		nbit
    //

    u_long one = (u_long) 1 << nb;
    int carry;

    carry = __addl(man[nl], one, &man[nl]);

    nl--;

    for (; nl >= 0 && carry; nl--) {
	carry = (u_long) __addl(man[nl], (u_long) 1, &man[nl]);
    }

    return carry;
}




/***
* _RoundMan -  round mantissa
*
*Purpose:
*   round mantissa to nbit precision
*
*
*Entry:
*   man: mantissa in internal form
*   precision: number of bits to be kept after rounding
*
*Exit:
*   returns 1 on overflow, 0 otherwise
*
*Exceptions:
*
*******************************************************************************/

int _RoundMan (intrnman man, int precision)
{
    int i,rndbit,nl,nb;
    u_long rndmask;
    int nbit;
    int retval = 0;

    //
    // The order of the n'th bit is n-1, since the first bit is bit 0
    // therefore decrement precision to get the order of the last bit
    // to be kept
    //
    nbit = precision - 1;

    rndbit = nbit+1;

    nl = rndbit / 32;
    nb = 31 - rndbit % 32;

    //
    // Get value of round bit
    //

    rndmask = (u_long)1 << nb;

    if ((man[nl] & rndmask) &&
	 !_ZeroTail(man, rndbit)) {

	//
	// round up
	//

	retval = _IncMan(man, nbit);
    }


    //
    // fill rest of mantissa with zeroes
    //

    man[nl] &= MAX_ULONG << nb;
    for(i=nl+1; i<INTRNMAN_LEN; i++) {
	man[i] = (u_long)0;
    }

    return retval;
}


/***
* _CopyMan - copy mantissa
*
*Purpose:
*    copy src to dest
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/
void _CopyMan (intrnman dest, intrnman src)
{
    u_long *p, *q;
    int i;

    p = src;
    q = dest;

    for (i=0; i < INTRNMAN_LEN; i++) {
	*q++ = *p++;
    }
}



/***
* _FillZeroMan - fill mantissa with zeroes
*
*Purpose:
*
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/
void _FillZeroMan(intrnman man)
{
    int i;
    for (i=0; i < INTRNMAN_LEN; i++)
	man[i] = (u_long)0;
}



/***
* _IsZeroMan - check if mantissa is zero
*
*Purpose:
*
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/
int _IsZeroMan(intrnman man)
{
    int i;
    for (i=0; i < INTRNMAN_LEN; i++)
	if (man[i])
	    return 0;

    return 1;
}





/***
* _ShrMan - shift mantissa to the right
*
*Purpose:
*  shift man by n bits to the right
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/
void _ShrMan (intrnman man, int n)
{
    int i, n1, n2, mask;
    int carry_from_left;

    //
    // declare this as volatile in order to work around a C8
    // optimization bug
    //

    volatile int carry_to_right;

    n1 = n / 32;
    n2 = n % 32;

    mask = ~(MAX_ULONG << n2);


    //
    // first deal with shifts by less than 32 bits
    //

    carry_from_left = 0;
    for (i=0; i<INTRNMAN_LEN; i++) {

	carry_to_right = man[i] & mask;

	man[i] >>= n2;

	man[i] |= carry_from_left;

	carry_from_left = carry_to_right << (32 - n2);
    }


    //
    // now shift whole 32-bit ints
    //

    for (i=INTRNMAN_LEN-1; i>=0; i--) {
	if (i >= n1) {
	    man[i] = man[i-n1];
	}
	else {
	    man[i] = 0;
	}
    }
}




/***
* _ld12tocvt - _LDBL12 floating point conversion
*
*Purpose:
*   convert a internal _LBL12 structure into an IEEE floating point
*   representation
*
*
*Entry:
*   pld12:  pointer to the _LDBL12
*   format: pointer to the format descriptor structure
*
*Exit:
*   *d contains the IEEE representation
*   returns the INTRNCVT_STATUS
*
*Exceptions:
*
*******************************************************************************/
INTRNCVT_STATUS _ld12cvt(_LDBL12 *pld12, void *d, FpFormatDescriptor *format)
{
    u_long man[INTRNMAN_LEN];
    u_long saved_man[INTRNMAN_LEN];
    u_long msw;
    unsigned int bexp;			// biased exponent
    int exp_shift;
    int exponent, sign;
    INTRNCVT_STATUS retval;

    exponent = (*U_EXP_12(pld12) & 0x7fff) - 0x3fff;   // unbias exponent
    sign = *U_EXP_12(pld12) & 0x8000;


    man[0] = *UL_MANHI_12(pld12);
    man[1] = *UL_MANLO_12(pld12);
    man[2] = *U_XT_12(pld12) << 16;


    //
    // bexp is the final biased value of the exponent to be used
    // Each of the following blocks should provide appropriate
    // values for man, bexp and retval. The mantissa is also
    // shifted to the right, leaving space for the exponent
    // and sign to be inserted
    //

    if (exponent == 0 - 0x3fff) {

	// either a denormal or zero
	bexp = 0;

	if (_IsZeroMan(man)) {

	    retval = INTRNCVT_OK;
	}
	else {

	    _FillZeroMan(man);

	    // denormal has been flushed to zero

	    retval = INTRNCVT_UNDERFLOW;
	}
    }
    else {

	// save mantissa in case it needs to be rounded again
	// at a different point (e.g., if the result is a denormal)

	_CopyMan(saved_man, man);

	if (_RoundMan(man, format->precision)) {
	    exponent ++;
	}

	if (exponent < format->min_exp - format->precision ) {

	    //
	    // underflow that produces a zero
	    //

	    _FillZeroMan(man);
	    bexp = 0;
	    retval = INTRNCVT_UNDERFLOW;
	}

	else if (exponent <= format->min_exp) {

	    //
	    // underflow that produces a denormal
	    //
	    //

	    // The (unbiased) exponent will be MIN_EXP
	    // Find out how much the mantissa should be shifted
	    // One shift is done implicitly by moving the
	    // binary point one bit to the left, i.e.,
	    // we treat the mantissa as .ddddd instead of d.dddd
	    // (where d is a binary digit)

	    int shift = format->min_exp - exponent;

	    // The mantissa should be rounded again, so it
	    // has to be restored

	    _CopyMan(man,saved_man);

	    _ShrMan(man, shift);
	    _RoundMan(man, format->precision); // need not check for carry

	    // make room for the exponent + sign

	    _ShrMan(man, format->exp_width + 1);

	    bexp = 0;
	    retval = INTRNCVT_UNDERFLOW;

	}

	else if (exponent >= format->max_exp) {

	    //
	    // overflow, return infinity
	    //

	    _FillZeroMan(man);
	    man[0] |= (1 << 31); // set MSB

	    // make room for the exponent + sign

	    _ShrMan(man, (format->exp_width + 1) - 1);

	    bexp = format->max_exp + format->bias;

	    retval = INTRNCVT_OVERFLOW;
	}

	else {

	    //
	    // valid, normalized result
	    //

	    bexp = exponent + format->bias;


	    // clear implied bit

	    man[0] &= (~( 1 << 31));

	    //
	    // shift right to make room for exponent + sign
	    //

	    _ShrMan(man, (format->exp_width + 1) - 1);

	    retval = INTRNCVT_OK;

	}
    }


    exp_shift = 32 - (format->exp_width + 1);
    msw =  man[0] |
	   (bexp << exp_shift) |
	   (sign ? 1<<31 : 0);

    if (format->format_width == 64) {

	*UL_HI_D(d) = msw;
	*UL_LO_D(d) = man[1];
    }

    else if (format->format_width == 32) {

	*(u_long *)d = msw;

    }

    return retval;
}


/***
* _ld12tod - convert _LDBL12 to double
*
*Purpose:
*
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/
INTRNCVT_STATUS _ld12tod(_LDBL12 *pld12, DOUBLE *d)
{
    return _ld12cvt(pld12, d, &DoubleFormat);
}



/***
* _ld12tof - convert _LDBL12 to float
*
*Purpose:
*
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/
INTRNCVT_STATUS _ld12tof(_LDBL12 *pld12, FLOAT *f)
{
    return _ld12cvt(pld12, f, &FloatFormat);
}


/***
* _ld12told - convert _LDBL12 to 80 bit long double
*
*Purpose:
*
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/
INTRNCVT_STATUS _ld12told(_LDBL12 *pld12, _LDOUBLE *pld)
{

    //
    // This implementation is based on the fact that the _LDBL12 format is
    // identical to the long double and has 2 extra bytes of mantissa
    //

    u_short exp, sign;
    u_long man[INTRNMAN_LEN];
    INTRNCVT_STATUS retval = 0;

    exp = *U_EXP_12(pld12) & (u_short)0x7fff;
    sign = *U_EXP_12(pld12) & (u_short)0x8000;

    man[0] = *UL_MANHI_12(pld12);
    man[1] = *UL_MANLO_12(pld12);
    man[2] = *U_XT_12(pld12) << 16;

    if (_RoundMan(man, 64)) {
	// The MSB of the mantissa is explicit and should be 1
	// since we had a carry, the mantissa is now 0.
	man[0] = MSB_ULONG;
	exp ++;
    }

    if (exp == 0x7fff)
	retval = INTRNCVT_OVERFLOW;

    *UL_MANHI_LD(pld) = man[0];
    *UL_MANLO_LD(pld) = man[1];
    *U_EXP_LD(pld) = sign | exp;

    return retval;
}


void _atodbl(DOUBLE *d, char *str)
{
    const char *EndPtr;
    _LDBL12 ld12;

    __strgtold12(&ld12, &EndPtr, str, 0, 0, 0, 0 );
    _ld12tod(&ld12, d);
}


void _atoldbl(_LDOUBLE *ld, char *str)
{
    const char *EndPtr;
    _LDBL12 ld12;

    __strgtold12(&ld12, &EndPtr, str, 1, 0, 0, 0 );
    _ld12told(&ld12, ld);
}


void _atoflt(FLOAT *f, char *str)
{
    const char *EndPtr;
    _LDBL12 ld12;

    __strgtold12(&ld12, &EndPtr, str, 0, 0, 0, 0 );
    _ld12tof(&ld12, f);
}