xref: /plan9-contrib/sys/src/cmd/unix/drawterm/libc/fltfmt.c (revision 0d601874851962e88c6c60fdd2e637bba04e13c2)
1 /*
2  * The authors of this software are Rob Pike and Ken Thompson.
3  *              Copyright (c) 2002 by Lucent Technologies.
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose without fee is hereby granted, provided that this entire notice
6  * is included in all copies of any software which is or includes a copy
7  * or modification of this software and in all copies of the supporting
8  * documentation for such software.
9  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
10  * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
11  * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
12  * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
13  */
14 #include <u.h>
15 #include <libc.h>
16 #include <float.h>
17 #include <ctype.h>
18 #include "fmtdef.h"
19 
20 enum
21 {
22 	FDIGIT	= 30,
23 	FDEFLT	= 6,
24 	NSIGNIF	= 17
25 };
26 
27 /*
28  * first few powers of 10, enough for about 1/2 of the
29  * total space for doubles.
30  */
31 static double pows10[] =
32 {
33 	  1e0,   1e1,   1e2,   1e3,   1e4,   1e5,   1e6,   1e7,   1e8,   1e9,
34 	 1e10,  1e11,  1e12,  1e13,  1e14,  1e15,  1e16,  1e17,  1e18,  1e19,
35 	 1e20,  1e21,  1e22,  1e23,  1e24,  1e25,  1e26,  1e27,  1e28,  1e29,
36 	 1e30,  1e31,  1e32,  1e33,  1e34,  1e35,  1e36,  1e37,  1e38,  1e39,
37 	 1e40,  1e41,  1e42,  1e43,  1e44,  1e45,  1e46,  1e47,  1e48,  1e49,
38 	 1e50,  1e51,  1e52,  1e53,  1e54,  1e55,  1e56,  1e57,  1e58,  1e59,
39 	 1e60,  1e61,  1e62,  1e63,  1e64,  1e65,  1e66,  1e67,  1e68,  1e69,
40 	 1e70,  1e71,  1e72,  1e73,  1e74,  1e75,  1e76,  1e77,  1e78,  1e79,
41 	 1e80,  1e81,  1e82,  1e83,  1e84,  1e85,  1e86,  1e87,  1e88,  1e89,
42 	 1e90,  1e91,  1e92,  1e93,  1e94,  1e95,  1e96,  1e97,  1e98,  1e99,
43 	1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109,
44 	1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119,
45 	1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129,
46 	1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139,
47 	1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149,
48 	1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159,
49 };
50 
51 #undef pow10
52 #define  pow10(x)  fmtpow10(x)
53 
54 static double
55 pow10(int n)
56 {
57 	double d;
58 	int neg;
59 
60 	neg = 0;
61 	if(n < 0){
62 		if(n < DBL_MIN_10_EXP){
63 			return 0.;
64 		}
65 		neg = 1;
66 		n = -n;
67 	}else if(n > DBL_MAX_10_EXP){
68 		return HUGE_VAL;
69 	}
70 	if(n < (int)(sizeof(pows10)/sizeof(pows10[0])))
71 		d = pows10[n];
72 	else{
73 		d = pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
74 		for(;;){
75 			n -= sizeof(pows10)/sizeof(pows10[0]) - 1;
76 			if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))){
77 				d *= pows10[n];
78 				break;
79 			}
80 			d *= pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
81 		}
82 	}
83 	if(neg){
84 		return 1./d;
85 	}
86 	return d;
87 }
88 
89 static int
90 xadd(char *a, int n, int v)
91 {
92 	char *b;
93 	int c;
94 
95 	if(n < 0 || n >= NSIGNIF)
96 		return 0;
97 	for(b = a+n; b >= a; b--) {
98 		c = *b + v;
99 		if(c <= '9') {
100 			*b = c;
101 			return 0;
102 		}
103 		*b = '0';
104 		v = 1;
105 	}
106 	*a = '1';	/* overflow adding */
107 	return 1;
108 }
109 
110 static int
111 xsub(char *a, int n, int v)
112 {
113 	char *b;
114 	int c;
115 
116 	for(b = a+n; b >= a; b--) {
117 		c = *b - v;
118 		if(c >= '0') {
119 			*b = c;
120 			return 0;
121 		}
122 		*b = '9';
123 		v = 1;
124 	}
125 	*a = '9';	/* underflow subtracting */
126 	return 1;
127 }
128 
129 static void
130 xdtoa(Fmt *fmt, char *s2, double f)
131 {
132 	char s1[NSIGNIF+10];
133 	double g, h;
134 	int e, d, i, n;
135 	int c1, c2, c3, c4, ucase, sign, chr, prec;
136 
137 	prec = FDEFLT;
138 	if(fmt->flags & FmtPrec)
139 		prec = fmt->prec;
140 	if(prec > FDIGIT)
141 		prec = FDIGIT;
142 	if(__isNaN(f)) {
143 		strcpy(s2, "NaN");
144 		return;
145 	}
146 	if(__isInf(f, 1)) {
147 		strcpy(s2, "+Inf");
148 		return;
149 	}
150 	if(__isInf(f, -1)) {
151 		strcpy(s2, "-Inf");
152 		return;
153 	}
154 	sign = 0;
155 	if(f < 0) {
156 		f = -f;
157 		sign++;
158 	}
159 	ucase = 0;
160 	chr = fmt->r;
161 	if(isupper(chr)) {
162 		ucase = 1;
163 		chr = tolower(chr);
164 	}
165 
166 	e = 0;
167 	g = f;
168 	if(g != 0) {
169 		frexp(f, &e);
170 		e = e * .301029995664;
171 		if(e >= -150 && e <= +150) {
172 			d = 0;
173 			h = f;
174 		} else {
175 			d = e/2;
176 			h = f * pow10(-d);
177 		}
178 		g = h * pow10(d-e);
179 		while(g < 1) {
180 			e--;
181 			g = h * pow10(d-e);
182 		}
183 		while(g >= 10) {
184 			e++;
185 			g = h * pow10(d-e);
186 		}
187 	}
188 
189 	/*
190 	 * convert NSIGNIF digits and convert
191 	 * back to get accuracy.
192 	 */
193 	for(i=0; i<NSIGNIF; i++) {
194 		d = g;
195 		s1[i] = d + '0';
196 		g = (g - d) * 10;
197 	}
198 	s1[i] = 0;
199 
200 	/*
201 	 * try decimal rounding to eliminate 9s
202 	 */
203 	c2 = prec + 1;
204 	if(chr == 'f')
205 		c2 += e;
206 	if(c2 >= NSIGNIF-2) {
207 		strcpy(s2, s1);
208 		d = e;
209 		s1[NSIGNIF-2] = '0';
210 		s1[NSIGNIF-1] = '0';
211 		sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
212 		g = strtod(s1, nil);
213 		if(g == f)
214 			goto found;
215 		if(xadd(s1, NSIGNIF-3, 1)) {
216 			e++;
217 			sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
218 		}
219 		g = strtod(s1, nil);
220 		if(g == f)
221 			goto found;
222 		strcpy(s1, s2);
223 		e = d;
224 	}
225 
226 	/*
227 	 * convert back so s1 gets exact answer
228 	 */
229 	for(;;) {
230 		sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
231 		g = strtod(s1, nil);
232 		if(f > g) {
233 			if(xadd(s1, NSIGNIF-1, 1))
234 				e--;
235 			continue;
236 		}
237 		if(f < g) {
238 			if(xsub(s1, NSIGNIF-1, 1))
239 				e++;
240 			continue;
241 		}
242 		break;
243 	}
244 
245 found:
246 	/*
247 	 * sign
248 	 */
249 	d = 0;
250 	i = 0;
251 	if(sign)
252 		s2[d++] = '-';
253 	else if(fmt->flags & FmtSign)
254 		s2[d++] = '+';
255 	else if(fmt->flags & FmtSpace)
256 		s2[d++] = ' ';
257 
258 	/*
259 	 * copy into final place
260 	 * c1 digits of leading '0'
261 	 * c2 digits from conversion
262 	 * c3 digits of trailing '0'
263 	 * c4 digits after '.'
264 	 */
265 	c1 = 0;
266 	c2 = prec + 1;
267 	c3 = 0;
268 	c4 = prec;
269 	switch(chr) {
270 	default:
271 		if(xadd(s1, c2, 5))
272 			e++;
273 		break;
274 	case 'g':
275 		/*
276 		 * decide on 'e' of 'f' style convers
277 		 */
278 		if(xadd(s1, c2, 5))
279 			e++;
280 		if(e >= -5 && e <= prec) {
281 			c1 = -e - 1;
282 			c4 = prec - e;
283 			chr = 'h';	// flag for 'f' style
284 		}
285 		break;
286 	case 'f':
287 		if(xadd(s1, c2+e, 5))
288 			e++;
289 		c1 = -e;
290 		if(c1 > prec)
291 			c1 = c2;
292 		c2 += e;
293 		break;
294 	}
295 
296 	/*
297 	 * clean up c1 c2 and c3
298 	 */
299 	if(c1 < 0)
300 		c1 = 0;
301 	if(c2 < 0)
302 		c2 = 0;
303 	if(c2 > NSIGNIF) {
304 		c3 = c2-NSIGNIF;
305 		c2 = NSIGNIF;
306 	}
307 
308 	/*
309 	 * copy digits
310 	 */
311 	while(c1 > 0) {
312 		if(c1+c2+c3 == c4)
313 			s2[d++] = '.';
314 		s2[d++] = '0';
315 		c1--;
316 	}
317 	while(c2 > 0) {
318 		if(c2+c3 == c4)
319 			s2[d++] = '.';
320 		s2[d++] = s1[i++];
321 		c2--;
322 	}
323 	while(c3 > 0) {
324 		if(c3 == c4)
325 			s2[d++] = '.';
326 		s2[d++] = '0';
327 		c3--;
328 	}
329 
330 	/*
331 	 * strip trailing '0' on g conv
332 	 */
333 	if(fmt->flags & FmtSharp) {
334 		if(0 == c4)
335 			s2[d++] = '.';
336 	} else
337 	if(chr == 'g' || chr == 'h') {
338 		for(n=d-1; n>=0; n--)
339 			if(s2[n] != '0')
340 				break;
341 		for(i=n; i>=0; i--)
342 			if(s2[i] == '.') {
343 				d = n;
344 				if(i != n)
345 					d++;
346 				break;
347 			}
348 	}
349 	if(chr == 'e' || chr == 'g') {
350 		if(ucase)
351 			s2[d++] = 'E';
352 		else
353 			s2[d++] = 'e';
354 		c1 = e;
355 		if(c1 < 0) {
356 			s2[d++] = '-';
357 			c1 = -c1;
358 		} else
359 			s2[d++] = '+';
360 		if(c1 >= 100) {
361 			s2[d++] = c1/100 + '0';
362 			c1 = c1%100;
363 		}
364 		s2[d++] = c1/10 + '0';
365 		s2[d++] = c1%10 + '0';
366 	}
367 	s2[d] = 0;
368 }
369 
370 static int
371 floatfmt(Fmt *fmt, double f)
372 {
373 	char s[341];		/* precision+exponent+sign+'.'+null */
374 
375 	xdtoa(fmt, s, f);
376 	fmt->flags &= FmtWidth|FmtLeft;
377 	__fmtcpy(fmt, s, strlen(s), strlen(s));
378 	return 0;
379 }
380 
381 int
382 __efgfmt(Fmt *f)
383 {
384 	double d;
385 
386 	d = va_arg(f->args, double);
387 	return floatfmt(f, d);
388 }
389