xref: /reactos/sdk/lib/crt/misc/i10output.c (revision 299e4305)
1 #include <precomp.h>
2 
3 // From Wine msvcrt.h
4 typedef struct {ULONG x80[3];} MSVCRT__LDOUBLE; /* Intel 80 bit FP format has sizeof() 12 */
5 enum fpmod {
6     FP_ROUND_ZERO, /* only used when dropped part contains only zeros */
7     FP_ROUND_DOWN,
8     FP_ROUND_EVEN,
9     FP_ROUND_UP,
10     FP_VAL_INFINITY,
11     FP_VAL_NAN
12 };
13 struct fpnum {
14     int sign;
15     int exp;
16     ULONGLONG m;
17     enum fpmod mod;
18 };
19 
20 // From wine bnum.h
21 #define EXP_BITS 11
22 #define MANT_BITS 53
23 
24 int fpnum_double(struct fpnum *fp, double *d)
25 {
26     ULONGLONG bits = 0;
27 
28     if (fp->mod == FP_VAL_INFINITY)
29     {
30         *d = fp->sign * INFINITY;
31         return 0;
32     }
33 
34     if (fp->mod == FP_VAL_NAN)
35     {
36         bits = ~0;
37         if (fp->sign == 1)
38             bits &= ~((ULONGLONG)1 << (MANT_BITS + EXP_BITS - 1));
39         *d = *(double*)&bits;
40         return 0;
41     }
42 
43     TRACE("%c %#I64x *2^%d (round %d)\n", fp->sign == -1 ? '-' : '+',
44         fp->m, fp->exp, fp->mod);
45     if (!fp->m)
46     {
47         *d = fp->sign * 0.0;
48         return 0;
49     }
50 
51     /* make sure that we don't overflow modifying exponent */
52     if (fp->exp > 1<<EXP_BITS)
53     {
54         *d = fp->sign * INFINITY;
55         return ERANGE;
56     }
57     if (fp->exp < -(1<<EXP_BITS))
58     {
59         *d = fp->sign * 0.0;
60         return ERANGE;
61     }
62     fp->exp += MANT_BITS - 1;
63 
64     /* normalize mantissa */
65     while(fp->m < (ULONGLONG)1 << (MANT_BITS-1))
66     {
67         fp->m <<= 1;
68         fp->exp--;
69     }
70     while(fp->m >= (ULONGLONG)1 << MANT_BITS)
71     {
72         if (fp->m & 1 || fp->mod != FP_ROUND_ZERO)
73         {
74             if (!(fp->m & 1)) fp->mod = FP_ROUND_DOWN;
75             else if(fp->mod == FP_ROUND_ZERO) fp->mod = FP_ROUND_EVEN;
76             else fp->mod = FP_ROUND_UP;
77         }
78         fp->m >>= 1;
79         fp->exp++;
80     }
81     fp->exp += (1 << (EXP_BITS-1)) - 1;
82 
83     /* handle subnormals */
84     if (fp->exp <= 0)
85     {
86         if (fp->m & 1 && fp->mod == FP_ROUND_ZERO) fp->mod = FP_ROUND_EVEN;
87         else if (fp->m & 1) fp->mod = FP_ROUND_UP;
88         else if (fp->mod != FP_ROUND_ZERO) fp->mod = FP_ROUND_DOWN;
89         fp->m >>= 1;
90     }
91     while(fp->m && fp->exp<0)
92     {
93         if (fp->m & 1 && fp->mod == FP_ROUND_ZERO) fp->mod = FP_ROUND_EVEN;
94         else if (fp->m & 1) fp->mod = FP_ROUND_UP;
95         else if (fp->mod != FP_ROUND_ZERO) fp->mod = FP_ROUND_DOWN;
96         fp->m >>= 1;
97         fp->exp++;
98     }
99 
100     /* round mantissa */
101     if (fp->mod == FP_ROUND_UP || (fp->mod == FP_ROUND_EVEN && fp->m & 1))
102     {
103         fp->m++;
104 
105         /* handle subnormal that falls into regular range due to rounding */
106         if (fp->m == (ULONGLONG)1 << (MANT_BITS - 1))
107         {
108             fp->exp++;
109         }
110         else if (fp->m >= (ULONGLONG)1 << MANT_BITS)
111         {
112             fp->exp++;
113             fp->m >>= 1;
114         }
115     }
116 
117     if (fp->exp >= (1<<EXP_BITS)-1)
118     {
119         *d = fp->sign * INFINITY;
120         return ERANGE;
121     }
122     if (!fp->m || fp->exp < 0)
123     {
124         *d = fp->sign * 0.0;
125         return ERANGE;
126     }
127 
128     if (fp->sign == -1)
129         bits |= (ULONGLONG)1 << (MANT_BITS + EXP_BITS - 1);
130     bits |= (ULONGLONG)fp->exp << (MANT_BITS - 1);
131     bits |= fp->m & (((ULONGLONG)1 << (MANT_BITS - 1)) - 1);
132 
133     TRACE("returning %#I64x\n", bits);
134     *d = *(double*)&bits;
135     return 0;
136 }
137 
138 #define I10_OUTPUT_MAX_PREC 21
139 /* Internal structure used by $I10_OUTPUT */
140 struct _I10_OUTPUT_DATA {
141     short pos;
142     char sign;
143     BYTE len;
144     char str[I10_OUTPUT_MAX_PREC+1]; /* add space for '\0' */
145 };
146 
147 /*********************************************************************
148  *              $I10_OUTPUT (MSVCRT.@)
149  * ld80 - long double (Intel 80 bit FP in 12 bytes) to be printed to data
150  * prec - precision of part, we're interested in
151  * flag - 0 for first prec digits, 1 for fractional part
152  * data - data to be populated
153  *
154  * return value
155  *      0 if given double is NaN or INF
156  *      1 otherwise
157  *
158  * FIXME
159  *      Native sets last byte of data->str to '0' or '9', I don't know what
160  *      it means. Current implementation sets it always to '0'.
161  */
162 int CDECL I10_OUTPUT(MSVCRT__LDOUBLE ld80, int prec, int flag, struct _I10_OUTPUT_DATA *data)
163 {
164     struct fpnum num;
165     double d;
166     char format[8];
167     char buf[I10_OUTPUT_MAX_PREC+9]; /* 9 = strlen("0.e+0000") + '\0' */
168     char *p;
169 
170     if ((ld80.x80[2] & 0x7fff) == 0x7fff)
171     {
172         if (ld80.x80[0] == 0 && ld80.x80[1] == 0x80000000)
173             strcpy( data->str, "1#INF" );
174         else
175             strcpy( data->str, (ld80.x80[1] & 0x40000000) ? "1#QNAN" : "1#SNAN" );
176         data->pos = 1;
177         data->sign = (ld80.x80[2] & 0x8000) ? '-' : ' ';
178         data->len = (BYTE)strlen(data->str);
179         return 0;
180     }
181 
182     num.sign = (ld80.x80[2] & 0x8000) ? -1 : 1;
183     num.exp  = (ld80.x80[2] & 0x7fff) - 0x3fff - 63;
184     num.m    = ld80.x80[0] | ((ULONGLONG)ld80.x80[1] << 32);
185     num.mod  = FP_ROUND_EVEN;
186     fpnum_double( &num, &d );
187     TRACE("(%lf %d %x %p)\n", d, prec, flag, data);
188 
189     if(d<0) {
190         data->sign = '-';
191         d = -d;
192     } else
193         data->sign = ' ';
194 
195     if(flag&1) {
196         int exp = 1 + floor(log10(d));
197 
198         prec += exp;
199         if(exp < 0)
200             prec--;
201     }
202     prec--;
203 
204     if(prec+1 > I10_OUTPUT_MAX_PREC)
205         prec = I10_OUTPUT_MAX_PREC-1;
206     else if(prec < 0) {
207         d = 0.0;
208         prec = 0;
209     }
210 
211     sprintf_s(format, sizeof(format), "%%.%dle", prec);
212     sprintf_s(buf, sizeof(buf), format, d);
213 
214     buf[1] = buf[0];
215     data->pos = atoi(buf+prec+3);
216     if(buf[1] != '0')
217         data->pos++;
218 
219     for(p = buf+prec+1; p>buf+1 && *p=='0'; p--);
220     data->len = p-buf;
221 
222     memcpy(data->str, buf+1, data->len);
223     data->str[data->len] = '\0';
224 
225     if(buf[1]!='0' && prec-data->len+1>0)
226         memcpy(data->str+data->len+1, buf+data->len+1, prec-data->len+1);
227 
228     return 1;
229 }
230 #undef I10_OUTPUT_MAX_PREC
231