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