You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
347 lines
10 KiB
347 lines
10 KiB
/***
|
|
*x10fout.c - floating point output for 10-byte long double
|
|
*
|
|
* Copyright (c) 1991-2001, Microsoft Corporation. All rights reserved.
|
|
*
|
|
*Purpose:
|
|
* Support conversion of a long double into a string
|
|
*
|
|
*Revision History:
|
|
* 07/15/91 GDP Initial version in C (ported from assembly)
|
|
* 01/23/92 GDP Support MIPS encoding for NaN
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include <string.h>
|
|
#include <cv.h>
|
|
|
|
#define STRCPY strcpy
|
|
|
|
#define PUT_ZERO_FOS(fos) \
|
|
fos->exp = 0, \
|
|
fos->sign = ' ', \
|
|
fos->ManLen = 1, \
|
|
fos->man[0] = '0',\
|
|
fos->man[1] = 0;
|
|
|
|
#define SNAN_STR "1#SNAN"
|
|
#define SNAN_STR_LEN 6
|
|
#define QNAN_STR "1#QNAN"
|
|
#define QNAN_STR_LEN 6
|
|
#define INF_STR "1#INF"
|
|
#define INF_STR_LEN 5
|
|
#define IND_STR "1#IND"
|
|
#define IND_STR_LEN 5
|
|
|
|
|
|
/***
|
|
*int _CALLTYPE5
|
|
* _$i10_output(_LDOUBLE ld,
|
|
* int ndigits,
|
|
* unsigned output_flags,
|
|
* FOS *fos) - output conversion of a 10-byte _LDOUBLE
|
|
*
|
|
*Purpose:
|
|
* Fill in a FOS structure for a given _LDOUBLE
|
|
*
|
|
*Entry:
|
|
* _LDOUBLE ld: The long double to be converted into a string
|
|
* int ndigits: number of digits allowed in the output format.
|
|
* unsigned output_flags: The following flags can be used:
|
|
* SO_FFORMAT: Indicates 'f' format
|
|
* (default is 'e' format)
|
|
* FOS *fos: the structure that i10_output will fill in
|
|
*
|
|
*Exit:
|
|
* modifies *fos
|
|
* return 1 if original number was ok, 0 otherwise (infinity, NaN, etc)
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
|
|
int _CALLTYPE5 $I10_OUTPUT(_LDOUBLE ld, int ndigits,
|
|
unsigned output_flags, FOS *fos)
|
|
{
|
|
u_short expn;
|
|
u_long manhi,manlo;
|
|
u_short sign;
|
|
|
|
/* useful constants (see algorithm explanation below) */
|
|
u_short const log2hi = 0x4d10;
|
|
u_short const log2lo = 0x4d;
|
|
u_short const log4hi = 0x9a;
|
|
u_long const c = 0x134312f4;
|
|
#if defined(L_END)
|
|
_LDBL12 ld12_one_tenth = {
|
|
{0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,
|
|
0xcc,0xcc,0xcc,0xcc,0xfb,0x3f}
|
|
};
|
|
#elif defined(B_END)
|
|
_LDBL12 ld12_one_tenth = {
|
|
{0x3f,0xfb,0xcc,0xcc,0xcc,0xcc,
|
|
0xcc,0xcc,0xcc,0xcc,0xcc,0xcc}
|
|
};
|
|
#endif
|
|
|
|
_LDBL12 ld12; /* space for a 12-byte long double */
|
|
_LDBL12 tmp12;
|
|
u_short hh,ll; /* the bytes of the exponent grouped in 2 words*/
|
|
u_short mm; /* the two MSBytes of the mantissa */
|
|
s_long r; /* the corresponding power of 10 */
|
|
s_short ir; /* ir = floor(r) */
|
|
int retval = 1; /* assume valid number */
|
|
char round; /* an additional character at the end of the string */
|
|
char *p;
|
|
int i;
|
|
int ub_exp;
|
|
int digcount;
|
|
|
|
/* grab the components of the long double */
|
|
expn = *U_EXP_LD(&ld);
|
|
manhi = *UL_MANHI_LD(&ld);
|
|
manlo = *UL_MANLO_LD(&ld);
|
|
sign = expn & MSB_USHORT;
|
|
expn &= 0x7fff;
|
|
|
|
if (sign)
|
|
fos->sign = '-';
|
|
else
|
|
fos->sign = ' ';
|
|
|
|
if (expn==0 && manhi==0 && manlo==0) {
|
|
PUT_ZERO_FOS(fos);
|
|
return 1;
|
|
}
|
|
|
|
if (expn == 0x7fff) {
|
|
fos->exp = 1; /* set a positive exponent for proper output */
|
|
|
|
/* check for special cases */
|
|
if (_IS_MAN_SNAN(sign, manhi, manlo)) {
|
|
/* signaling NAN */
|
|
STRCPY(fos->man,SNAN_STR);
|
|
fos->ManLen = SNAN_STR_LEN;
|
|
retval = 0;
|
|
}
|
|
else if (_IS_MAN_IND(sign, manhi, manlo)) {
|
|
/* indefinite */
|
|
STRCPY(fos->man,IND_STR);
|
|
fos->ManLen = IND_STR_LEN;
|
|
retval = 0;
|
|
}
|
|
else if (_IS_MAN_INF(sign, manhi, manlo)) {
|
|
/* infinity */
|
|
STRCPY(fos->man,INF_STR);
|
|
fos->ManLen = INF_STR_LEN;
|
|
retval = 0;
|
|
}
|
|
else {
|
|
/* quiet NAN */
|
|
STRCPY(fos->man,QNAN_STR);
|
|
fos->ManLen = QNAN_STR_LEN;
|
|
retval = 0;
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* Algorithm for the decoding of a valid real number x
|
|
*
|
|
* In the following INT(r) is the largest integer less than or
|
|
* equal to r (i.e. r rounded toward -infinity). We want a result
|
|
* r equal to 1 + log(x), because then x = mantissa
|
|
* * 10^(INT(r)) so that .1 <= mantissa < 1. Unfortunately,
|
|
* we cannot compute s exactly so we must alter the procedure
|
|
* slightly. We will instead compute an estimate r of 1 +
|
|
* log(x) which is always low. This will either result
|
|
* in the correctly normalized number on the top of the stack
|
|
* or perhaps a number which is a factor of 10 too large. We
|
|
* will then check to see that if x is larger than one
|
|
* and if so multiply x by 1/10.
|
|
*
|
|
* We will use a low precision (fixed point 24 bit) estimate
|
|
* of of 1 + log base 10 of x. We have approximately .mm
|
|
* * 2^hhll on the top of the stack where m, h, and l represent
|
|
* hex digits, mm represents the high 2 hex digits of the
|
|
* mantissa, hh represents the high 2 hex digits of the exponent,
|
|
* and ll represents the low 2 hex digits of the exponent. Since
|
|
* .mm is a truncated representation of the mantissa, using it
|
|
* in this monotonically increasing polynomial approximation
|
|
* of the logarithm will naturally give a low result. Let's
|
|
* derive a formula for a lower bound r on 1 + log(x):
|
|
*
|
|
* .4D104D42H < log(2)=.30102999...(base 10) < .4D104D43H
|
|
* .9A20H < log(4)=.60205999...(base 10) < .9A21H
|
|
*
|
|
* 1/2 <= .mm < 1
|
|
* ==> log(.mm) >= .mm * log(4) - log(4)
|
|
*
|
|
* Substituting in truncated hex constants in the formula above
|
|
* gives r = 1 + .4D104DH * hhll. + .9AH * .mm - .9A21H. Now
|
|
* multiplication of hex digits 5 and 6 of log(2) by ll has an
|
|
* insignificant effect on the first 24 bits of the result so
|
|
* it will not be calculated. This gives the expression r =
|
|
* 1 + .4D10H * hhll. + .4DH * .hh + .9A * .mm - .9A21H.
|
|
* Finally we must add terms to our formula to subtract out the
|
|
* effect of the exponent bias. We obtain the following formula:
|
|
*
|
|
* (implied decimal point)
|
|
* < >.< >
|
|
* |3|3|2|2|2|2|2|2|2|2|2|2|1|1|1|1|1|1|1|1|1|1|0|0|0|0|0|0|0|0|0|0|
|
|
* |1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|
|
|
* + < 1 >
|
|
* + < .4D10H * hhll. >
|
|
* + < .00004DH * hh00. >
|
|
* + < .9AH * .mm >
|
|
* - < .9A21H >
|
|
* - < .4D10H * 3FFEH >
|
|
* - < .00004DH * 3F00H >
|
|
*
|
|
* ==> r = .4D10H * hhll. + .4DH * .hh + .9AH * .mm - 1343.12F4H
|
|
*
|
|
* The difference between the lower bound r and the upper bound
|
|
* s is calculated as follows:
|
|
*
|
|
* .937EH < 1/ln(10)-log(1/ln(4))=.57614993...(base 10) < .937FH
|
|
*
|
|
* 1/2 <= .mm < 1
|
|
* ==> log(.mm) <= .mm * log(4) - [1/ln(10) - log(1/ln(4))]
|
|
*
|
|
* so tenatively s = r + log(4) - [1/ln(10) - log(1/ln(4))],
|
|
* but we must also add in terms to ensure we will have an upper
|
|
* bound even after the truncation of various values. Because
|
|
* log(2) * hh00. is truncated to .4D104DH * hh00. we must
|
|
* add .0043H, because log(2) * ll. is truncated to .4D10H *
|
|
* ll. we must add .0005H, because <mantissa> * log(4) is
|
|
* truncated to .mm * .9AH we must add .009AH and .0021H.
|
|
*
|
|
* Thus s = r - .937EH + .9A21H + .0043H + .0005H + .009AH + .0021H
|
|
* = r + .07A6H
|
|
* ==> s = .4D10H * hhll. + .4DH * .hh + .9AH * .mm - 1343.0B4EH
|
|
*
|
|
* r is equal to 1 + log(x) more than (10000H - 7A6H) /
|
|
* 10000H = 97% of the time.
|
|
*
|
|
* In the above formula, a u_long is use to accomodate r, and
|
|
* there is an implied decimal point in the middle.
|
|
*/
|
|
|
|
hh = expn >> 8;
|
|
ll = expn & (u_short)0xff;
|
|
mm = (u_short) (manhi >> 24);
|
|
r = (s_long)log2hi*(s_long)expn + log2lo*hh + log4hi*mm - c;
|
|
ir = (s_short)(r >> 16);
|
|
|
|
/*
|
|
*
|
|
* We stated that we wanted to normalize x so that
|
|
*
|
|
* .1 <= x < 1
|
|
*
|
|
* This was a slight oversimplification. Actually we want a
|
|
* number which when rounded to 16 significant digits is in the
|
|
* desired range. To do this we must normalize x so that
|
|
*
|
|
* .1 - 5*10^(-18) <= x < 1 - 5*10^(-17)
|
|
*
|
|
* and then round.
|
|
*
|
|
* If we had f = INT(1+log(x)) we could multiply by 10^(-f)
|
|
* to get x into the desired range. We do not quite have
|
|
* f but we do have INT(r) from the last step which is equal
|
|
* to f 97% of the time and 1 less than f the rest of the time.
|
|
* We can multiply by 10^-[INT(r)] and if the result is greater
|
|
* than 1 - 5*10^(-17) we can then multiply by 1/10. This final
|
|
* result will lie in the proper range.
|
|
*/
|
|
|
|
/* convert _LDOUBLE to _LDBL12) */
|
|
*U_EXP_12(&ld12) = expn;
|
|
*UL_MANHI_12(&ld12) = manhi;
|
|
*UL_MANLO_12(&ld12) = manlo;
|
|
*U_XT_12(&ld12) = 0;
|
|
|
|
/* multiply by 10^(-ir) */
|
|
__multtenpow12(&ld12,-ir,1);
|
|
|
|
/* if ld12 >= 1.0 then divide by 10.0 */
|
|
if (*U_EXP_12(&ld12) >= 0x3fff) {
|
|
ir++;
|
|
__ld12mul(&ld12,&ld12_one_tenth);
|
|
}
|
|
|
|
fos->exp = ir;
|
|
if (output_flags & SO_FFORMAT){
|
|
/* 'f' format, add exponent to ndigits */
|
|
ndigits += ir;
|
|
if (ndigits <= 0) {
|
|
/* return 0 */
|
|
PUT_ZERO_FOS(fos);
|
|
return 1;
|
|
}
|
|
}
|
|
if (ndigits > MAX_MAN_DIGITS)
|
|
ndigits = MAX_MAN_DIGITS;
|
|
|
|
ub_exp = *U_EXP_12(&ld12) - 0x3ffe; /* unbias exponent */
|
|
*U_EXP_12(&ld12) = 0;
|
|
|
|
/*
|
|
* Now the mantissa has to be converted to fixed point.
|
|
* Then we will use the MSB of ld12 for generating
|
|
* the decimal digits. The next 11 bytes will hold
|
|
* the mantissa (after it has been converted to
|
|
* fixed point).
|
|
*/
|
|
|
|
for (i=0;i<8;i++)
|
|
__shl_12(&ld12); /* make space for an extra byte,
|
|
in case we shift right later */
|
|
if (ub_exp < 0) {
|
|
int shift_count = (-ub_exp) & 0xff;
|
|
for (;shift_count>0;shift_count--)
|
|
__shr_12(&ld12);
|
|
}
|
|
|
|
p = fos->man;
|
|
for(digcount=ndigits+1;digcount>0;digcount--) {
|
|
tmp12 = ld12;
|
|
__shl_12(&ld12);
|
|
__shl_12(&ld12);
|
|
__add_12(&ld12,&tmp12);
|
|
__shl_12(&ld12); /* ld12 *= 10 */
|
|
|
|
/* Now we have the first decimal digit in the msbyte of exponent */
|
|
*p++ = (char) (*UCHAR_12(&ld12,11) + '0');
|
|
*UCHAR_12(&ld12,11) = 0;
|
|
}
|
|
|
|
round = *(--p);
|
|
p--; /* p points now to the last character of the string
|
|
excluding the rounding digit */
|
|
if (round >= '5') {
|
|
/* look for a non-9 digit starting from the end of string */
|
|
for (;p>=fos->man && *p=='9';p--) {
|
|
*p = '0';
|
|
}
|
|
if (p < fos->man){
|
|
p++;
|
|
fos->exp ++;
|
|
}
|
|
(*p)++;
|
|
}
|
|
else {
|
|
/* remove zeros */
|
|
for (;p>=fos->man && *p=='0';p--);
|
|
if (p < fos->man) {
|
|
/* return 0 */
|
|
PUT_ZERO_FOS(fos);
|
|
return 1;
|
|
}
|
|
}
|
|
fos->ManLen = (char) (p - fos->man + 1);
|
|
fos->man[fos->ManLen] = '\0';
|
|
}
|
|
return retval;
|
|
}
|