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