windows-nt/Source/XPSP1/NT/base/crts/fpw32/conv/x10fout.c

348 lines
10 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/***
*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;
}