466 lines
14 KiB
C
466 lines
14 KiB
C
/*
|
|
*x10fout.c - floating point output for 10-byte long double
|
|
|
|
* Copyright (c) 1991-1991, 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
|
|
* 05-26-92 GWK Windbg srcs
|
|
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "mathsup.h"
|
|
|
|
|
|
|
|
#define STRCPY strcpy
|
|
|
|
#define PUT_ZERO_FOS(fos) \
|
|
fos->exp = 1, \
|
|
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
|
|
|
|
#define MAX_10_LEN 30 //max length of string including NULL
|
|
|
|
/*
|
|
char * _uldtoa (_ULDOUBLE *px,
|
|
* int maxchars,
|
|
* char *ldtext)
|
|
|
|
|
|
*Purpose:
|
|
* Return pointer to filled in string "ldtext" for
|
|
* a given _UDOUBLE ponter px
|
|
* with a maximum character width of maxchars
|
|
|
|
*Entry:
|
|
* _ULDOUBLE * px: a pointer to the long double to be converted into a string
|
|
* int maxchars: number of digits allowed in the output format.
|
|
|
|
* (default is 'e' format)
|
|
|
|
* char * ldtext: a pointer to the output string
|
|
|
|
*Exit:
|
|
* returns pointer to the output string
|
|
|
|
*Exceptions:
|
|
|
|
*/
|
|
|
|
|
|
char * _uldtoa(_ULDOUBLE *px, int maxchars, char *ldtext)
|
|
{
|
|
char in_str[250];
|
|
char in_str2[250];
|
|
char cExp[20];
|
|
FOS foss;
|
|
char * lpszMan;
|
|
char * lpIndx;
|
|
int nErr;
|
|
int len1, len2;
|
|
|
|
maxchars -= 8; /* sign, dot, E+0001 */
|
|
|
|
nErr = $I10_OUTPUT(*px, maxchars, 0, &foss);
|
|
|
|
lpszMan = foss.man;
|
|
|
|
ldtext[0] = foss.sign;
|
|
ldtext[1] = *lpszMan;
|
|
ldtext[2] = '.';
|
|
ldtext[3] = '\0';
|
|
|
|
maxchars += 2; /* sign, dot */
|
|
|
|
lpszMan++;
|
|
strcat(ldtext, lpszMan);
|
|
|
|
len1 = strlen(ldtext); // for 'e'
|
|
|
|
|
|
strcpy(cExp, "e");
|
|
|
|
foss.exp -= 1; /* Adjust for the shift decimal shift above */
|
|
_itoa(foss.exp, in_str, 10);
|
|
|
|
|
|
if (foss.exp < 0) {
|
|
strcat(cExp, "-");
|
|
|
|
strcpy(in_str2, &in_str[1]);
|
|
strcpy(in_str, in_str2);
|
|
|
|
while (strlen(in_str) < 4) {
|
|
strcpy(in_str2, in_str);
|
|
strcpy(in_str, "0");
|
|
strcat(in_str, in_str2);
|
|
}
|
|
}
|
|
else {
|
|
while (strlen(in_str) < 4) {
|
|
strcpy(in_str2, in_str);
|
|
strcpy(in_str, "0");
|
|
strcat(in_str, in_str2);
|
|
}
|
|
}
|
|
|
|
if (foss.exp >= 0) {
|
|
strcat(cExp, "+");
|
|
}
|
|
|
|
strcat(cExp, in_str);
|
|
|
|
len2 = strlen(cExp);
|
|
|
|
if (len1 == maxchars) {
|
|
;
|
|
}
|
|
else if (len1 < maxchars) {
|
|
do {
|
|
strcat(ldtext, "0");
|
|
len1++;
|
|
} while (len1 < maxchars);
|
|
}
|
|
else {
|
|
lpIndx = &ldtext[len1 - 1]; // point to last char and round
|
|
do {
|
|
*lpIndx = '\0';
|
|
lpIndx--;
|
|
len1--; //NOTENOTE v-griffk we really need to round
|
|
} while (len1 > maxchars);
|
|
}
|
|
|
|
strcat(ldtext, cExp);
|
|
return ldtext;
|
|
}
|
|
|
|
|
|
/*
|
|
*int _$i10_output(_ULDOUBLE ld,
|
|
* int ndigits,
|
|
* unsigned output_flags,
|
|
* FOS *fos) - output conversion of a 10-byte _ULDOUBLE
|
|
|
|
*Purpose:
|
|
* Fill in a FOS structure for a given _ULDOUBLE
|
|
|
|
*Entry:
|
|
* _ULDOUBLE 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 $I10_OUTPUT(_ULDOUBLE 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)
|
|
_ULDBL12 ld12_one_tenth = {
|
|
{ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
|
|
0xcc, 0xcc, 0xcc, 0xcc, 0xfb, 0x3f }
|
|
};
|
|
#elif defined(B_END)
|
|
_ULDBL12 ld12_one_tenth = {
|
|
{ 0x3f, 0xfb, 0xcc, 0xcc, 0xcc, 0xcc,
|
|
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc }
|
|
};
|
|
#endif
|
|
|
|
_ULDBL12 ld12; /* space for a 12-byte long double */
|
|
_ULDBL12 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 _ULDOUBLE to _ULDBL12) */
|
|
*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;
|
|
}
|