1 /* @(#)fconv.c 1.45 10/11/06 Copyright 1985, 1995-2010 J. Schilling */
2 /*
3 * Convert floating point numbers to strings for format.c
4 * Should rather use the MT-safe routines [efg]convert()
5 *
6 * Copyright (c) 1985, 1995-2010 J. Schilling
7 */
8 /*
9 * The contents of this file are subject to the terms of the
10 * Common Development and Distribution License, Version 1.0 only
11 * (the "License"). You may not use this file except in compliance
12 * with the License.
13 *
14 * See the file CDDL.Schily.txt in this distribution for details.
15 *
16 * When distributing Covered Code, include this CDDL HEADER in each
17 * file and include the License file CDDL.Schily.txt from this distribution.
18 */
19
20 #include <schily/mconfig.h> /* <- may define NO_FLOATINGPOINT */
21 #ifndef NO_FLOATINGPOINT
22
23 #ifndef __DO_LONG_DOUBLE__
24 #include <schily/stdlib.h>
25 #include <schily/standard.h>
26 #include <schily/string.h>
27 #include <schily/schily.h>
28 #include <schily/math.h> /* The default place for isinf()/isnan() */
29 #include <schily/nlsdefs.h>
30
31 #if !defined(HAVE_STDLIB_H) || defined(HAVE_DTOA)
32 extern char *ecvt __PR((double, int, int *, int *));
33 extern char *fcvt __PR((double, int, int *, int *));
34 #endif
35
36 #if defined(HAVE_ISNAN) && defined(HAVE_ISINF)
37 /*
38 * *BSD alike libc
39 */
40 #define FOUND_ISNAN
41 #define FOUND_ISINF
42 #define FOUND_ISXX
43 #endif
44
45 #if defined(HAVE_C99_ISNAN) && defined(HAVE_C99_ISINF)
46 #ifndef FOUND_ISXX
47 #define FOUND_ISXX
48 #endif
49 #define FOUND_C99_ISNAN
50 #define FOUND_C99_ISINF
51 #define FOUND_C99_ISXX
52 #endif
53
54 #if defined(HAVE_FP_H) && !defined(FOUND_ISXX)
55 /*
56 * WAS:
57 * #if defined(__osf__) || defined(_IBMR2) || defined(_AIX)
58 */
59 /*
60 * Moved before HAVE_IEEEFP_H for True64 due to a hint
61 * from Bert De Knuydt <Bert.Deknuydt@esat.kuleuven.ac.be>
62 *
63 * True64 has both fp.h & ieeefp.h but the functions
64 * isnand() & finite() seem to be erreneously not implemented
65 * as a macro and the function lives in libm.
66 * Let's hope that we will not get problems with the new order.
67 */
68 #include <fp.h>
69 #if !defined(isnan) && defined(IS_NAN)
70 #define isnan IS_NAN
71 #define FOUND_ISNAN
72 #endif
73 #if !defined(isinf) && defined(FINITE)
74 #define isinf !FINITE
75 #define FOUND_ISINF
76 #endif
77 #if defined(FOUND_ISNAN) && defined(FOUND_ISINF)
78 #define FOUND_ISXX
79 #endif
80 #endif
81
82 #if defined(HAVE_IEEEFP_H) && !defined(FOUND_ISXX) && \
83 !defined(FOUND_C99_ISXX)
84 /*
85 * SVR4
86 */
87 #include <ieeefp.h>
88 #ifdef HAVE_ISNAND
89 #ifndef isnan
90 #define isnan isnand
91 #define FOUND_ISNAN
92 #endif
93 #endif
94 #ifdef HAVE_FINITE
95 #ifndef isinf
96 #define isinf !finite
97 #define FOUND_ISINF
98 #endif
99 #endif
100 #if defined(FOUND_ISNAN) && defined(FOUND_ISINF)
101 #define FOUND_ISXX
102 #endif
103 #endif
104
105 /*
106 * WAS:
107 * #if defined(__hpux) || defined(VMS) || defined(_SCO_DS) || defined(__QNX__)
108 */
109 #ifdef __nneded__
110 #if defined(__hpux) || defined(__QNX__) || defined(__DJGPP__)
111 #ifndef FOUND_C99_ISXX
112 #undef isnan
113 #undef isinf
114 #endif
115 #endif
116 #endif /* __needed__ */
117
118 /*
119 * As we no longer check for defined(isnan)/defined(isinf), the next block
120 * should also handle the problems with DJGPP, HP-UX, QNX and VMS.
121 */
122 #if !defined(FOUND_ISNAN) && !defined(HAVE_C99_ISNAN)
123 #undef isnan
124 #define isnan(val) (0)
125 #define NO_ISNAN
126 #endif
127 #if !defined(FOUND_ISINF) && !defined(HAVE_C99_ISINF)
128 #undef isinf
129 #define isinf(val) (0)
130 #define NO_ISINF
131 #endif
132
133 #if defined(NO_ISNAN) || defined(NO_ISINF)
134 #include <schily/float.h> /* For values.h */
135 #if (_IEEE - 0) > 0 /* We know that there is IEEE FP */
136 /*
137 * Note that older HP-UX versions have different #defines for MAXINT in
138 * values.h and sys/param.h
139 */
140 #include <schily/utypes.h>
141 #include <schily/btorder.h>
142
143 #ifdef WORDS_BIGENDIAN
144 #define fpw_high(x) ((UInt32_t *)&x)[0]
145 #define fpw_low(x) ((UInt32_t *)&x)[1]
146 #else
147 #define fpw_high(x) ((UInt32_t *)&x)[1]
148 #define fpw_low(x) ((UInt32_t *)&x)[0]
149 #endif
150 #define FP_EXP 0x7FF00000
151 #define fp_exp(x) (fpw_high(x) & FP_EXP)
152 #define fp_exc(x) (fp_exp(x) == FP_EXP)
153
154 #ifdef NO_ISNAN
155 #undef isnan
156 #define isnan(val) (fp_exc(val) && \
157 (fpw_low(val) != 0 || (fpw_high(val) & 0xFFFFF) != 0))
158 #endif
159 #ifdef NO_ISINF
160 #undef isinf
161 #define isinf(val) (fp_exc(val) && \
162 fpw_low(val) == 0 && (fpw_high(val) & 0xFFFFF) == 0)
163 #endif
164 #endif /* We know that there is IEEE FP */
165 #endif /* defined(NO_ISNAN) || defined(NO_ISINF) */
166
167
168 #if !defined(HAVE_ECVT) || !defined(HAVE_FCVT) || !defined(HAVE_GCVT)
169
170 #ifdef NO_USER_XCVT
171 /*
172 * We cannot define our own ecvt()/fcvt()/gcvt() so we need to use
173 * local names instead.
174 */
175 #ifndef HAVE_ECVT
176 #define ecvt js_ecvt
177 #endif
178 #ifndef HAVE_FCVT
179 #define fcvt js_fcvt
180 #endif
181 #ifndef HAVE_GCVT
182 #define gcvt js_gcvt
183 #endif
184 #endif
185
186 #include "cvt.c"
187 #endif
188
189 static char _js_nan[] = "(NaN)";
190 static char _js_inf[] = "(Infinity)";
191
192 static int _ferr __PR((char *, double));
193 #endif /* __DO_LONG_DOUBLE__ */
194
195 #ifdef __DO_LONG_DOUBLE__
196 #undef MDOUBLE
197 #define MDOUBLE long double
198 #else
199 #undef MDOUBLE
200 #define MDOUBLE double
201 #endif
202
203 #ifdef abs
204 #undef abs
205 #endif
206 #define abs(i) ((i) < 0 ? -(i) : (i))
207
208 EXPORT int
ftoes(s,val,fieldwidth,ndigits)209 ftoes(s, val, fieldwidth, ndigits)
210 register char *s;
211 MDOUBLE val;
212 register int fieldwidth;
213 register int ndigits;
214 {
215 register char *b;
216 register char *rs;
217 register int len;
218 register int rdecpt;
219 int decpt;
220 int sign;
221
222 #ifndef __DO_LONG_DOUBLE__
223 if ((len = _ferr(s, val)) > 0)
224 return (len);
225 #endif
226 rs = s;
227 #ifdef V7_FLOATSTYLE
228 b = ecvt(val, ndigits, &decpt, &sign);
229 rdecpt = decpt;
230 #else
231 b = ecvt(val, ndigits+1, &decpt, &sign);
232 rdecpt = decpt-1;
233 #endif
234 #ifdef __DO_LONG_DOUBLE__
235 len = *b;
236 if (len < '0' || len > '9') { /* Inf/NaN */
237 strcpy(s, b);
238 return (strlen(b));
239 }
240 #endif
241 len = ndigits + 6; /* Punkt e +/- nnn */
242 if (sign)
243 len++;
244 if (fieldwidth > len)
245 while (fieldwidth-- > len)
246 *rs++ = ' ';
247 if (sign)
248 *rs++ = '-';
249 #ifndef V7_FLOATSTYLE
250 if (*b)
251 *rs++ = *b++;
252 #endif
253 #if defined(HAVE_LOCALECONV) && defined(USE_LOCALE)
254 *rs++ = *(localeconv()->decimal_point);
255 #else
256 *rs++ = '.';
257 #endif
258 while (*b && ndigits-- > 0)
259 *rs++ = *b++;
260 *rs++ = 'e';
261 *rs++ = rdecpt >= 0 ? '+' : '-';
262 rdecpt = abs(rdecpt);
263 #ifdef __DO_LONG_DOUBLE__
264 if (rdecpt >= 1000) { /* Max-Exp is > 4000 */
265 *rs++ = rdecpt / 1000 + '0';
266 rdecpt %= 1000;
267 }
268 #endif
269 #ifndef V7_FLOATSTYLE
270 if (rdecpt >= 100)
271 #endif
272 {
273 *rs++ = rdecpt / 100 + '0';
274 rdecpt %= 100;
275 }
276 *rs++ = rdecpt / 10 + '0';
277 *rs++ = rdecpt % 10 + '0';
278 *rs = '\0';
279 return (rs - s);
280 }
281
282 /*
283 * fcvt() from Cygwin32 is buggy.
284 */
285 #if !defined(HAVE_FCVT) && defined(HAVE_ECVT)
286 #define USE_ECVT
287 #endif
288
289 EXPORT int
ftofs(s,val,fieldwidth,ndigits)290 ftofs(s, val, fieldwidth, ndigits)
291 register char *s;
292 MDOUBLE val;
293 register int fieldwidth;
294 register int ndigits;
295 {
296 register char *b;
297 register char *rs;
298 register int len;
299 register int rdecpt;
300 int decpt;
301 int sign;
302
303 #ifndef __DO_LONG_DOUBLE__
304 if ((len = _ferr(s, val)) > 0)
305 return (len);
306 #endif
307 rs = s;
308 #ifdef USE_ECVT
309 /*
310 * Needed on systems with broken fcvt() implementation
311 * (e.g. Cygwin32)
312 */
313 b = ecvt(val, ndigits, &decpt, &sign);
314 /*
315 * The next call is needed to force higher precision.
316 */
317 if (decpt > 0)
318 b = ecvt(val, ndigits+decpt, &decpt, &sign);
319 #else
320 b = fcvt(val, ndigits, &decpt, &sign);
321 #endif
322 #ifdef __DO_LONG_DOUBLE__
323 len = *b;
324 if (len < '0' || len > '9') { /* Inf/NaN */
325 strcpy(s, b);
326 return (strlen(b));
327 }
328 #endif
329 rdecpt = decpt;
330 len = rdecpt + ndigits + 1;
331 if (rdecpt < 0)
332 len -= rdecpt;
333 if (sign)
334 len++;
335 if (fieldwidth > len)
336 while (fieldwidth-- > len)
337 *rs++ = ' ';
338 if (sign)
339 *rs++ = '-';
340 if (rdecpt > 0) {
341 len = rdecpt;
342 while (*b && len-- > 0)
343 *rs++ = *b++;
344 #ifdef USE_ECVT
345 while (len-- > 0)
346 *rs++ = '0';
347 #endif
348 }
349 #ifndef V7_FLOATSTYLE
350 else {
351 *rs++ = '0';
352 }
353 #endif
354 #if defined(HAVE_LOCALECONV) && defined(USE_LOCALE)
355 *rs++ = *(localeconv()->decimal_point);
356 #else
357 *rs++ = '.';
358 #endif
359 if (rdecpt < 0) {
360 len = rdecpt;
361 while (len++ < 0 && ndigits-- > 0)
362 *rs++ = '0';
363 }
364 while (*b && ndigits-- > 0)
365 *rs++ = *b++;
366 #ifdef USE_ECVT
367 while (ndigits-- > 0)
368 *rs++ = '0';
369 #endif
370 *rs = '\0';
371 return (rs - s);
372 }
373
374 #ifndef __DO_LONG_DOUBLE__
375
376 #ifdef HAVE_LONGDOUBLE
377 #ifdef HAVE__LDECVT
378 #define qecvt(ld, n, dp, sp) _ldecvt(*(long_double *)&ld, n, dp, sp)
379 #endif
380 #ifdef HAVE__LDFCVT
381 #define qfcvt(ld, n, dp, sp) _ldfcvt(*(long_double *)&ld, n, dp, sp)
382 #endif
383
384 #if (defined(HAVE_QECVT) || defined(HAVE__LDECVT)) && \
385 (defined(HAVE_QFCVT) || defined(HAVE__LDFCVT))
386 #define __DO_LONG_DOUBLE__
387 #define ftoes qftoes
388 #define ftofs qftofs
389 #define ecvt qecvt
390 #define fcvt qfcvt
391 #include "fconv.c"
392 #undef __DO_LONG_DOUBLE__
393 #endif
394 #endif /* HAVE_LONGDOUBLE */
395
396 LOCAL int
_ferr(s,val)397 _ferr(s, val)
398 char *s;
399 double val;
400 {
401 if (isnan(val)) {
402 strcpy(s, _js_nan);
403 return (sizeof (_js_nan) - 1);
404 }
405
406 /*
407 * Check first for NaN because finite() will return 1 on Nan too.
408 */
409 if (isinf(val)) {
410 strcpy(s, _js_inf);
411 return (sizeof (_js_inf) - 1);
412 }
413 return (0);
414 }
415 #endif /* __DO_LONG_DOUBLE__ */
416 #endif /* NO_FLOATINGPOINT */
417