1 /* @(#)fconv.c 1.59 18/09/10 Copyright 1985, 1995-2018 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-2018 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 * A copy of the CDDL is also available via the Internet at
16 * http://www.opensource.org/licenses/cddl1.txt
17 *
18 * When distributing Covered Code, include this CDDL HEADER in each
19 * file and include the License file CDDL.Schily.txt from this distribution.
20 */
21
22 #include <schily/mconfig.h> /* <- may define NO_FLOATINGPOINT */
23 #ifndef NO_FLOATINGPOINT
24
25 #ifndef __DO_LONG_DOUBLE__
26 #include <schily/stdlib.h>
27 #include <schily/standard.h>
28 #include <schily/string.h>
29 #include <schily/schily.h>
30 #include <schily/math.h> /* The default place for isinf()/isnan() */
31 #include <schily/nlsdefs.h>
32 #include <schily/ctype.h>
33 #include "format.h"
34
35 #if !defined(HAVE_STDLIB_H) || defined(HAVE_DTOA)
36 extern char *ecvt __PR((double, int, int *, int *));
37 extern char *fcvt __PR((double, int, int *, int *));
38 #endif
39
40 #if defined(HAVE_ISNAN) && defined(HAVE_ISINF)
41 /*
42 * *BSD alike libc
43 */
44 #define FOUND_ISNAN
45 #define FOUND_ISINF
46 #define FOUND_ISXX
47 #endif
48
49 #if defined(HAVE_C99_ISNAN) && defined(HAVE_C99_ISINF)
50 #ifndef FOUND_ISXX
51 #define FOUND_ISXX
52 #endif
53 #define FOUND_C99_ISNAN
54 #define FOUND_C99_ISINF
55 #define FOUND_C99_ISXX
56 #endif
57
58 #if defined(HAVE_FP_H) && !defined(FOUND_ISXX)
59 /*
60 * WAS:
61 * #if defined(__osf__) || defined(_IBMR2) || defined(_AIX)
62 */
63 /*
64 * Moved before HAVE_IEEEFP_H for True64 due to a hint
65 * from Bert De Knuydt <Bert.Deknuydt@esat.kuleuven.ac.be>
66 *
67 * True64 has both fp.h & ieeefp.h but the functions
68 * isnand() & finite() seem to be erreneously not implemented
69 * as a macro and the function lives in libm.
70 * Let's hope that we will not get problems with the new order.
71 */
72 #include <fp.h>
73 #if !defined(isnan) && defined(IS_NAN)
74 #define isnan IS_NAN
75 #define FOUND_ISNAN
76 #endif
77 #if !defined(isinf) && defined(FINITE)
78 #define isinf !FINITE
79 #define FOUND_ISINF
80 #endif
81 #if defined(FOUND_ISNAN) && defined(FOUND_ISINF)
82 #define FOUND_ISXX
83 #endif
84 #endif
85
86 #if defined(HAVE_IEEEFP_H) && !defined(FOUND_ISXX) && \
87 !defined(FOUND_C99_ISXX)
88 /*
89 * SVR4
90 */
91 #include <ieeefp.h>
92 #ifdef HAVE_ISNAND
93 #ifndef isnan
94 #define isnan isnand
95 #define FOUND_ISNAN
96 #endif
97 #endif
98 #ifdef HAVE_FINITE
99 #ifndef isinf
100 #define isinf !finite
101 #define FOUND_ISINF
102 #endif
103 #endif
104 #if defined(FOUND_ISNAN) && defined(FOUND_ISINF)
105 #define FOUND_ISXX
106 #endif
107 #endif
108
109 /*
110 * WAS:
111 * #if defined(__hpux) || defined(VMS) || defined(_SCO_DS) || defined(__QNX__)
112 */
113 #ifdef __nneded__
114 #if defined(__hpux) || defined(__QNX__) || defined(__DJGPP__)
115 #ifndef FOUND_C99_ISXX
116 #undef isnan
117 #undef isinf
118 #endif
119 #endif
120 #endif /* __needed__ */
121
122 /*
123 * As we no longer check for defined(isnan)/defined(isinf), the next block
124 * should also handle the problems with DJGPP, HP-UX, QNX and VMS.
125 */
126 #if !defined(FOUND_ISNAN) && !defined(HAVE_C99_ISNAN)
127 #undef isnan
128 #define isnan(val) (0)
129 #define NO_ISNAN
130 #endif
131 #if !defined(FOUND_ISINF) && !defined(HAVE_C99_ISINF)
132 #undef isinf
133 #define isinf(val) (0)
134 #define NO_ISINF
135 #endif
136
137 #if defined(NO_ISNAN) || defined(NO_ISINF)
138 #include <schily/float.h> /* For values.h */
139 #if (_IEEE - 0) > 0 /* We know that there is IEEE FP */
140 /*
141 * Note that older HP-UX versions have different #defines for MAXINT in
142 * values.h and sys/param.h
143 */
144 #include <schily/utypes.h>
145 #include <schily/btorder.h>
146
147 #ifdef WORDS_BIGENDIAN
148 #define fpw_high(x) ((UInt32_t *)&x)[0]
149 #define fpw_low(x) ((UInt32_t *)&x)[1]
150 #else
151 #define fpw_high(x) ((UInt32_t *)&x)[1]
152 #define fpw_low(x) ((UInt32_t *)&x)[0]
153 #endif
154 #define FP_EXP 0x7FF00000
155 #define fp_exp(x) (fpw_high(x) & FP_EXP)
156 #define fp_exc(x) (fp_exp(x) == FP_EXP)
157
158 #ifdef NO_ISNAN
159 #undef isnan
160 #define isnan(val) (fp_exc(val) && \
161 (fpw_low(val) != 0 || (fpw_high(val) & 0xFFFFF) != 0))
162 #endif
163 #ifdef NO_ISINF
164 #undef isinf
165 #define isinf(val) (fp_exc(val) && \
166 fpw_low(val) == 0 && (fpw_high(val) & 0xFFFFF) == 0)
167 #endif
168 #endif /* We know that there is IEEE FP */
169 #endif /* defined(NO_ISNAN) || defined(NO_ISINF) */
170
171
172 #if !defined(HAVE_ECVT) || !defined(HAVE_FCVT) || !defined(HAVE_GCVT)
173
174 #ifdef NO_USER_XCVT
175 /*
176 * We cannot define our own ecvt()/fcvt()/gcvt() so we need to use
177 * local names instead.
178 */
179 #ifndef HAVE_ECVT
180 #define ecvt js_ecvt
181 #endif
182 #ifndef HAVE_FCVT
183 #define fcvt js_fcvt
184 #endif
185 #ifndef HAVE_GCVT
186 #define gcvt js_gcvt
187 #endif
188 #endif
189
190 #include "cvt.c"
191 #endif
192
193 #ifdef JOS_COMPAT
194 static char _js_nan[] = "(NaN)";
195 static char _js_inf[] = "(Infinity)";
196 #else
197 static char _js_nan[] = "nan";
198 static char _js_inf[] = "inf";
199 #endif
200
201 static int _ferr __PR((char *, double, int));
202
203 static int _ecv __PR((char *s, char *b, int fieldwidth, int ndigits,
204 int decpt, int sign, int flags));
205 static int _fcv __PR((char *s, char *b, int fieldwidth, int ndigits,
206 int decpt, int sign, int flags));
207
208 #endif /* __DO_LONG_DOUBLE__ */
209
210 #ifdef __DO_LONG_DOUBLE__
211 #undef MDOUBLE
212 #define MDOUBLE long double
213 #else
214 #undef MDOUBLE
215 #define MDOUBLE double
216 #endif
217
218 #ifdef abs
219 #undef abs
220 #endif
221 #define abs(i) ((i) < 0 ? -(i) : (i))
222
223 EXPORT int
ftoes(s,val,fieldwidth,ndigits)224 ftoes(s, val, fieldwidth, ndigits)
225 register char *s;
226 MDOUBLE val;
227 register int fieldwidth;
228 register int ndigits;
229 {
230 int flags = 0;
231
232 if (ndigits < 0) {
233 ndigits = -ndigits;
234 flags |= UPPERFLG;
235 }
236 return (_ftoes(s, val, fieldwidth, ndigits, flags));
237 }
238
239 EXPORT int
_ftoes(s,val,fieldwidth,ndigits,flags)240 _ftoes(s, val, fieldwidth, ndigits, flags)
241 register char *s;
242 MDOUBLE val;
243 register int fieldwidth;
244 register int ndigits;
245 int flags;
246 {
247 register char *b;
248 register int len;
249 register int rdecpt;
250 int decpt;
251 int sign;
252 #ifdef __DO_LONG_DOUBLE__
253 int Efmt = FALSE;
254
255 if (flags & UPPERFLG)
256 Efmt = TRUE;
257 #endif
258 #ifndef __DO_LONG_DOUBLE__
259 if ((len = _ferr(s, val, flags)) > 0)
260 return (len);
261 #endif
262 #ifdef V7_FLOATSTYLE
263 b = ecvt(val, ndigits, &decpt, &sign);
264 rdecpt = decpt;
265 #else
266 b = ecvt(val, ndigits+1, &decpt, &sign);
267 rdecpt = decpt-1;
268 #endif
269 #ifdef __DO_LONG_DOUBLE__
270 len = *b;
271 if (len == '-' || len == '+')
272 len = b[1];
273 if (len < '0' || len > '9') { /* Inf/NaN */
274 char *p;
275
276 for (p = b; *p; p++)
277 *s++ = Efmt ? toupper(*p) : *p;
278 *s = '\0';
279 return (strlen(b));
280 }
281 #endif
282 return (_ecv(s, b, fieldwidth, ndigits, rdecpt, sign, flags));
283 }
284
285 #ifndef __DO_LONG_DOUBLE__
286 int
_ecv(s,b,fieldwidth,ndigits,rdecpt,sign,flags)287 _ecv(s, b, fieldwidth, ndigits, rdecpt, sign, flags)
288 register char *s;
289 register char *b;
290 register int fieldwidth;
291 register int ndigits;
292 register int rdecpt;
293 int sign;
294 int flags;
295 {
296 register int len;
297 register char *rs = s;
298 char dpoint;
299 char *pp;
300
301 #if defined(HAVE_LOCALECONV) && defined(USE_LOCALE)
302 dpoint = *(localeconv()->decimal_point);
303 #else
304 dpoint = '.';
305 #endif
306 len = ndigits + 6; /* Punkt e +/- nnn */
307 if (sign)
308 len++;
309 else if (flags & PLUSFLG)
310 len++;
311 else if (flags & SPACEFLG)
312 len++;
313 if ((flags & PADZERO) == 0 && fieldwidth > len) {
314 while (fieldwidth-- > len)
315 *rs++ = ' ';
316 }
317 if (sign)
318 *rs++ = '-';
319 else if (flags & PLUSFLG)
320 *rs++ = '+';
321 else if (flags & SPACEFLG)
322 *rs++ = ' ';
323 if ((flags & PADZERO) && fieldwidth > len) {
324 while (fieldwidth-- > len)
325 *rs++ = '0';
326 }
327 #ifndef V7_FLOATSTYLE
328 if (*b)
329 *rs++ = *b++;
330 #endif
331 pp = rs;
332 if ((flags & HASHFLG) || ndigits != 0) /* Add '.'? */
333 *rs++ = dpoint;
334
335 while (*b && ndigits-- > 0)
336 *rs++ = *b++;
337 if (flags & STRIPZERO) {
338 register char *rpp = pp;
339
340 while (--rs > rpp) {
341 if (*rs != '0')
342 break;
343 }
344 if (*rs == dpoint)
345 rs--;
346 rs++;
347 }
348 if (flags & UPPERFLG)
349 *rs++ = 'E';
350 else
351 *rs++ = 'e';
352 *rs++ = rdecpt >= 0 ? '+' : '-';
353 rdecpt = abs(rdecpt);
354 #ifdef HAVE_LONGDOUBLE
355 if (rdecpt >= 1000) { /* Max-Exp is > 4000 */
356 *rs++ = rdecpt / 1000 + '0';
357 rdecpt %= 1000;
358 }
359 #endif
360 #ifndef V7_FLOATSTYLE
361 if (rdecpt >= 100)
362 #endif
363 {
364 *rs++ = rdecpt / 100 + '0';
365 rdecpt %= 100;
366 }
367 *rs++ = rdecpt / 10 + '0';
368 *rs++ = rdecpt % 10 + '0';
369 *rs = '\0';
370 return (rs - s);
371 }
372 #endif /* __DO_LONG_DOUBLE__ */
373
374
375 /*
376 * fcvt() from Cygwin32 is buggy.
377 */
378 #if !defined(HAVE_FCVT) && defined(HAVE_ECVT)
379 #define USE_ECVT
380 #endif
381
382 EXPORT int
ftofs(s,val,fieldwidth,ndigits)383 ftofs(s, val, fieldwidth, ndigits)
384 register char *s;
385 MDOUBLE val;
386 register int fieldwidth;
387 register int ndigits;
388 {
389 int flags = 0;
390
391 if (ndigits < 0) {
392 ndigits = -ndigits;
393 flags |= UPPERFLG;
394 }
395 return (_ftofs(s, val, fieldwidth, ndigits, flags));
396 }
397
398 EXPORT int
_ftofs(s,val,fieldwidth,ndigits,flags)399 _ftofs(s, val, fieldwidth, ndigits, flags)
400 register char *s;
401 MDOUBLE val;
402 register int fieldwidth;
403 register int ndigits;
404 int flags;
405 {
406 register char *b;
407 register int len;
408 int decpt;
409 int sign;
410 #ifdef __DO_LONG_DOUBLE__
411 int Ffmt = FALSE;
412
413 if (flags & UPPERFLG)
414 Ffmt = TRUE;
415 #endif
416 #ifndef __DO_LONG_DOUBLE__
417 if ((len = _ferr(s, val, flags)) > 0)
418 return (len);
419 #endif
420 #ifdef USE_ECVT
421 /*
422 * Needed on systems with broken fcvt() implementation
423 * (e.g. Cygwin32)
424 */
425 b = ecvt(val, ndigits, &decpt, &sign);
426 /*
427 * The next call is needed to force higher precision.
428 */
429 if (decpt > 0)
430 b = ecvt(val, ndigits+decpt, &decpt, &sign);
431 #else
432 b = fcvt(val, ndigits, &decpt, &sign);
433 #endif
434 #ifdef __DO_LONG_DOUBLE__
435 len = *b;
436 if (len == '-' || len == '+')
437 len = b[1];
438 if (len < '0' || len > '9') { /* Inf/NaN */
439 char *p;
440
441 for (p = b; *p; p++)
442 *s++ = Ffmt ? toupper(*p) : *p;
443 *s = '\0';
444 return (strlen(b));
445 }
446 #endif
447 return (_fcv(s, b, fieldwidth, ndigits, decpt, sign, flags));
448 }
449
450 #ifndef __DO_LONG_DOUBLE__
451 LOCAL int
_fcv(s,b,fieldwidth,ndigits,rdecpt,sign,flags)452 _fcv(s, b, fieldwidth, ndigits, rdecpt, sign, flags)
453 register char *s;
454 register char *b;
455 register int fieldwidth;
456 register int ndigits;
457 register int rdecpt;
458 int sign;
459 int flags;
460 {
461 register int len;
462 register char *rs = s;
463 char dpoint;
464 char *pp;
465
466 #if defined(HAVE_LOCALECONV) && defined(USE_LOCALE)
467 dpoint = *(localeconv()->decimal_point);
468 #else
469 dpoint = '.';
470 #endif
471
472 len = rdecpt + ndigits + 1;
473 if (rdecpt < 0)
474 len -= rdecpt;
475 if (sign)
476 len++;
477 else if (flags & PLUSFLG)
478 len++;
479 else if (flags & SPACEFLG)
480 len++;
481 if ((flags & PADZERO) == 0 && fieldwidth > len) {
482 while (fieldwidth-- > len)
483 *rs++ = ' ';
484 }
485 if (sign)
486 *rs++ = '-';
487 else if (flags & PLUSFLG)
488 *rs++ = '+';
489 else if (flags & SPACEFLG)
490 *rs++ = ' ';
491 if ((flags & PADZERO) && fieldwidth > len) {
492 while (fieldwidth-- > len)
493 *rs++ = '0';
494 }
495 if (rdecpt > 0) {
496 len = rdecpt;
497 while (*b && len-- > 0)
498 *rs++ = *b++;
499 #ifdef USE_ECVT
500 while (len-- > 0)
501 *rs++ = '0';
502 #endif
503 }
504 #ifndef V7_FLOATSTYLE
505 else {
506 *rs++ = '0';
507 }
508 #endif
509 pp = rs;
510 if ((flags & HASHFLG) || ndigits != 0) /* Add '.'? */
511 *rs++ = dpoint;
512
513 if (rdecpt < 0) {
514 len = rdecpt;
515 while (len++ < 0 && ndigits-- > 0)
516 *rs++ = '0';
517 }
518 while (*b && ndigits-- > 0)
519 *rs++ = *b++;
520 if (flags & STRIPZERO) {
521 register char *rpp = pp;
522
523 while (--rs > rpp) {
524 if (*rs != '0')
525 break;
526 }
527 if (*rs == dpoint)
528 rs--;
529 rs++;
530 } else {
531 #ifdef USE_ECVT
532 while (ndigits-- > 0)
533 *rs++ = '0';
534 #endif
535 }
536 *rs = '\0';
537 return (rs - s);
538 }
539 #endif /* __DO_LONG_DOUBLE__ */
540
541 EXPORT int
_ftogs(s,val,fieldwidth,ndigits,flags)542 _ftogs(s, val, fieldwidth, ndigits, flags)
543 register char *s;
544 MDOUBLE val;
545 register int fieldwidth;
546 register int ndigits;
547 int flags;
548 {
549 register char *b;
550 register int len;
551 int decpt;
552 int sign;
553 #ifdef __DO_LONG_DOUBLE__
554 int Gfmt = FALSE;
555
556 if (flags & UPPERFLG)
557 Gfmt = TRUE;
558 #endif
559 #ifndef __DO_LONG_DOUBLE__
560 if ((len = _ferr(s, val, flags)) > 0)
561 return (len);
562 #endif
563 b = ecvt(val, ndigits, &decpt, &sign);
564 #ifdef __DO_LONG_DOUBLE__
565 len = *b;
566 if (len == '-' || len == '+')
567 len = b[1];
568 if (len < '0' || len > '9') { /* Inf/NaN */
569 char *p;
570
571 for (p = b; *p; p++)
572 *s++ = Gfmt ? toupper(*p) : *p;
573 *s = '\0';
574 return (strlen(b));
575 }
576 #endif
577 if ((flags & HASHFLG) == 0)
578 flags |= STRIPZERO;
579 #ifdef V7_FLOATSTYLE
580 if ((decpt >= 0 && decpt-ndigits > 4) ||
581 #else
582 if ((decpt >= 0 && decpt-ndigits > 0) ||
583 #endif
584 (decpt < -3)) { /* e-format */
585 decpt--;
586 return (_ecv(s, b, fieldwidth, ndigits, decpt, sign, flags));
587 } else { /* f-format */
588 ndigits -= decpt;
589 return (_fcv(s, b, fieldwidth, ndigits, decpt, sign, flags));
590 }
591 }
592
593
594 #ifndef __DO_LONG_DOUBLE__
595
596 #ifdef HAVE_LONGDOUBLE
597 #ifdef HAVE__LDECVT
598 #define qecvt(ld, n, dp, sp) _ldecvt(*(long_double *)&ld, n, dp, sp)
599 #endif
600 #ifdef HAVE__LDFCVT
601 #define qfcvt(ld, n, dp, sp) _ldfcvt(*(long_double *)&ld, n, dp, sp)
602 #endif
603
604 #if (defined(HAVE_QECVT) || defined(HAVE__LDECVT)) && \
605 (defined(HAVE_QFCVT) || defined(HAVE__LDFCVT))
606 #define __DO_LONG_DOUBLE__
607 #define ftoes qftoes
608 #define ftofs qftofs
609 #define _ftoes _qftoes
610 #define _ftofs _qftofs
611 #define _ftogs _qftogs
612 #define ecvt qecvt
613 #define fcvt qfcvt
614 #include "fconv.c"
615 #undef __DO_LONG_DOUBLE__
616 #endif
617 #endif /* HAVE_LONGDOUBLE */
618
619 /*
620 * Be careful to avoid a GCC bug: while (*s) *s++ = toupper(*s) is converted
621 * to while (*s++) *s = toupper(*s).
622 */
623 LOCAL int
_ferr(s,val,flags)624 _ferr(s, val, flags)
625 char *s;
626 double val;
627 int flags;
628 {
629 char *old = s;
630 char *p;
631 int upper;
632
633 upper = flags & UPPERFLG;
634 if (isnan(val)) {
635 if (val < 0) /* Should not happen */
636 *s++ = '-';
637 else if (flags & PLUSFLG)
638 *s++ = '+';
639 for (p = _js_nan; *p; p++)
640 *s++ = upper ? toupper(*p) : *p;
641 *s = '\0';
642 return (s - old);
643 }
644
645 /*
646 * Check first for NaN because finite() will return 1 on Nan too.
647 */
648 if (isinf(val)) {
649 if (val < 0)
650 *s++ = '-';
651 else if (flags & PLUSFLG)
652 *s++ = '+';
653 for (p = _js_inf; *p; p++)
654 *s++ = upper ? toupper(*p) : *p;
655 *s = '\0';
656 return (s - old);
657 }
658 return (0);
659 }
660 #endif /* __DO_LONG_DOUBLE__ */
661 #endif /* NO_FLOATINGPOINT */
662