~ubuntu-branches/ubuntu/hardy/9base/hardy

« back to all changes in this revision

Viewing changes to lib9/fmt/fltfmt.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Baumann
  • Date: 2006-01-25 15:33:00 UTC
  • Revision ID: james.westby@ubuntu.com-20060125153300-6hh4p9wx8iqqply5
Tags: upstream-2
ImportĀ upstreamĀ versionĀ 2

Show diffs side-by-side

added added

removed removed

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