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