windows-nt/Source/XPSP1/NT/base/crts/fpw32/conv/intrncvt.c
2020-09-26 16:20:57 +08:00

716 lines
13 KiB
C

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