Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <ctype.h>
4
#include "fmtdef.h"
5
 
6
enum
7
{
8
	FDIGIT	= 30,
9
	FDEFLT	= 6,
10
	NSIGNIF	= 17,
11
	NEXP10	= 308,
12
};
13
 
14
static int
15
xadd(char *a, int n, int v)
16
{
17
	char *b;
18
	int c;
19
 
20
	if(n < 0 || n >= NSIGNIF)
21
		return 0;
22
	for(b = a+n; b >= a; b--) {
23
		c = *b + v;
24
		if(c <= '9') {
25
			*b = c;
26
			return 0;
27
		}
28
		*b = '0';
29
		v = 1;
30
	}
31
	*a = '1';	// overflow adding
32
	return 1;
33
}
34
 
35
static int
36
xsub(char *a, int n, int v)
37
{
38
	char *b;
39
	int c;
40
 
41
	for(b = a+n; b >= a; b--) {
42
		c = *b - v;
43
		if(c >= '0') {
44
			*b = c;
45
			return 0;
46
		}
47
		*b = '9';
48
		v = 1;
49
	}
50
	*a = '9';	// underflow subtracting
51
	return 1;
52
}
53
 
54
static void
55
xdtoa(Fmt *fmt, char *s2, double f)
56
{
57
	char s1[NSIGNIF+10];
58
	double g, h;
59
	int e, d, i, n;
60
	int c1, c2, c3, c4, ucase, sign, chr, prec;
61
 
62
	prec = FDEFLT;
63
	if(fmt->flags & FmtPrec)
64
		prec = fmt->prec;
65
	if(prec > FDIGIT)
66
		prec = FDIGIT;
67
	if(isNaN(f)) {
68
		strcpy(s2, "NaN");
69
		return;
70
	}
71
	if(isInf(f, 1)) {
72
		strcpy(s2, "+Inf");
73
		return;
74
	}
75
	if(isInf(f, -1)) {
76
		strcpy(s2, "-Inf");
77
		return;
78
	}
79
	sign = 0;
80
	if(f < 0) {
81
		f = -f;
82
		sign++;
83
	}
84
	ucase = 0;
85
	chr = fmt->r;
86
	if(isupper(chr)) {
87
		ucase = 1;
88
		chr = tolower(chr);
89
	}
90
 
91
	e = 0;
92
	g = f;
93
	if(g != 0) {
94
		frexp(f, &e);
95
		e = e * .301029995664;
96
		if(e >= -150 && e <= +150) {
97
			d = 0;
98
			h = f;
99
		} else {
100
			d = e/2;
101
			h = f * pow10(-d);
102
		}
103
		g = h * pow10(d-e);
104
		while(g < 1) {
105
			e--;
106
			g = h * pow10(d-e);
107
		}
108
		while(g >= 10) {
109
			e++;
110
			g = h * pow10(d-e);
111
		}
112
	}
113
 
114
	/*
115
	 * convert NSIGNIF digits and convert
116
	 * back to get accuracy.
117
	 */
118
	for(i=0; i<NSIGNIF; i++) {
119
		d = g;
120
		s1[i] = d + '0';
121
		g = (g - d) * 10;
122
	}
123
	s1[i] = 0;
124
 
125
	/*
126
	 * try decimal rounding to eliminate 9s
127
	 */
128
	c2 = prec + 1;
129
	if(chr == 'f')
130
		c2 += e;
131
	if(c2 >= NSIGNIF-2) {
132
		strcpy(s2, s1);
133
		d = e;
134
		s1[NSIGNIF-2] = '0';
135
		s1[NSIGNIF-1] = '0';
136
		sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
137
		g = strtod(s1, nil);
138
		if(g == f)
139
			goto found;
140
		if(xadd(s1, NSIGNIF-3, 1)) {
141
			e++;
142
			sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
143
		}
144
		g = strtod(s1, nil);
145
		if(g == f)
146
			goto found;
147
		strcpy(s1, s2);
148
		e = d;
149
	}
150
 
151
	/*
152
	 * convert back so s1 gets exact answer
153
	 */
154
	for(;;) {
155
		sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
156
		g = strtod(s1, nil);
157
		if(f > g) {
158
			if(xadd(s1, NSIGNIF-1, 1))
159
				e--;
160
			continue;
161
		}
162
		if(f < g) {
163
			if(xsub(s1, NSIGNIF-1, 1))
164
				e++;
165
			continue;
166
		}
167
		break;
168
	}
169
 
170
found:
171
	/*
172
	 * sign
173
	 */
174
	d = 0;
175
	i = 0;
176
	if(sign)
177
		s2[d++] = '-';
178
	else if(fmt->flags & FmtSign)
179
		s2[d++] = '+';
180
	else if(fmt->flags & FmtSpace)
181
		s2[d++] = ' ';
182
 
183
	/*
184
	 * copy into final place
185
	 * c1 digits of leading '0'
186
	 * c2 digits from conversion
187
	 * c3 digits of trailing '0'
188
	 * c4 digits after '.'
189
	 */
190
	c1 = 0;
191
	c2 = prec + 1;
192
	c3 = 0;
193
	c4 = prec;
194
	switch(chr) {
195
	default:
196
		if(xadd(s1, c2, 5))
197
			e++;
198
		break;
199
	case 'g':
200
		/*
201
		 * decide on 'e' of 'f' style convers
202
		 */
203
		if(xadd(s1, c2, 5))
204
			e++;
205
		if(e >= -5 && e <= prec) {
206
			c1 = -e - 1;
207
			c4 = prec - e;
208
			chr = 'h';	// flag for 'f' style
209
		}
210
		break;
211
	case 'f':
212
		if(xadd(s1, c2+e, 5))
213
			e++;
214
		c1 = -e;
215
		if(c1 > prec)
216
			c1 = c2;
217
		c2 += e;
218
		break;
219
	}
220
 
221
	/*
222
	 * clean up c1 c2 and c3
223
	 */
224
	if(c1 < 0)
225
		c1 = 0;
226
	if(c2 < 0)
227
		c2 = 0;
228
	if(c2 > NSIGNIF) {
229
		c3 = c2-NSIGNIF;
230
		c2 = NSIGNIF;
231
	}
232
 
233
	/*
234
	 * copy digits
235
	 */
236
	while(c1 > 0) {
237
		if(c1+c2+c3 == c4)
238
			s2[d++] = '.';
239
		s2[d++] = '0';
240
		c1--;
241
	}
242
	while(c2 > 0) {
243
		if(c2+c3 == c4)
244
			s2[d++] = '.';
245
		s2[d++] = s1[i++];
246
		c2--;
247
	}
248
	while(c3 > 0) {
249
		if(c3 == c4)
250
			s2[d++] = '.';
251
		s2[d++] = '0';
252
		c3--;
253
	}
254
 
255
	/*
256
	 * strip trailing '0' on g conv
257
	 */
258
	if(fmt->flags & FmtSharp) {
259
		if(0 == c4)
260
			s2[d++] = '.';
261
	} else
262
	if(chr == 'g' || chr == 'h') {
263
		for(n=d-1; n>=0; n--)
264
			if(s2[n] != '0')
265
				break;
266
		for(i=n; i>=0; i--)
267
			if(s2[i] == '.') {
268
				d = n;
269
				if(i != n)
270
					d++;
271
				break;
272
			}
273
	}
274
	if(chr == 'e' || chr == 'g') {
275
		if(ucase)
276
			s2[d++] = 'E';
277
		else
278
			s2[d++] = 'e';
279
		c1 = e;
280
		if(c1 < 0) {
281
			s2[d++] = '-';
282
			c1 = -c1;
283
		} else
284
			s2[d++] = '+';
285
		if(c1 >= 100) {
286
			s2[d++] = c1/100 + '0';
287
			c1 = c1%100;
288
		}
289
		s2[d++] = c1/10 + '0';
290
		s2[d++] = c1%10 + '0';
291
	}
292
	s2[d] = 0;
293
}
294
 
295
int
296
_floatfmt(Fmt *fmt, double f)
297
{
298
	char s[1+NEXP10+1+FDIGIT+1];
299
 
300
	/*
301
	 * The max length of a %f string is
302
	 *	'[+-]'+"max exponent"+'.'+"max precision"+'\0'
303
	 * which is 341 currently.
304
	 */	
305
	xdtoa(fmt, s, f);
306
	fmt->flags &= FmtWidth|FmtLeft;
307
	_fmtcpy(fmt, s, strlen(s), strlen(s));
308
	return 0;
309
}
310
 
311
int
312
_efgfmt(Fmt *f)
313
{
314
	double d;
315
 
316
	d = va_arg(f->args, double);
317
	return _floatfmt(f, d);
318
}