/*** * 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 #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>= 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); }