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