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
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
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
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