18ccd4a63SDavid du Colombier #include <u.h>
28ccd4a63SDavid du Colombier #include <libc.h>
3*0d601874SDavid du Colombier #include <float.h>
48ccd4a63SDavid du Colombier #include <ctype.h>
58ccd4a63SDavid du Colombier #include "fmtdef.h"
68ccd4a63SDavid du Colombier
78ccd4a63SDavid du Colombier enum
88ccd4a63SDavid du Colombier {
98ccd4a63SDavid du Colombier FDIGIT = 30,
108ccd4a63SDavid du Colombier FDEFLT = 6,
11*0d601874SDavid du Colombier NSIGNIF = 17
128ccd4a63SDavid du Colombier };
138ccd4a63SDavid du Colombier
14*0d601874SDavid du Colombier /*
15*0d601874SDavid du Colombier * first few powers of 10, enough for about 1/2 of the
16*0d601874SDavid du Colombier * total space for doubles.
17*0d601874SDavid du Colombier */
18*0d601874SDavid du Colombier static double pows10[] =
19*0d601874SDavid du Colombier {
20*0d601874SDavid du Colombier 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
21*0d601874SDavid du Colombier 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
22*0d601874SDavid du Colombier 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
23*0d601874SDavid du Colombier 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39,
24*0d601874SDavid du Colombier 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49,
25*0d601874SDavid du Colombier 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59,
26*0d601874SDavid du Colombier 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69,
27*0d601874SDavid du Colombier 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79,
28*0d601874SDavid du Colombier 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89,
29*0d601874SDavid du Colombier 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99,
30*0d601874SDavid du Colombier 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109,
31*0d601874SDavid du Colombier 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119,
32*0d601874SDavid du Colombier 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129,
33*0d601874SDavid du Colombier 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139,
34*0d601874SDavid du Colombier 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149,
35*0d601874SDavid du Colombier 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159,
36*0d601874SDavid du Colombier };
37*0d601874SDavid du Colombier
38*0d601874SDavid du Colombier #undef pow10
39*0d601874SDavid du Colombier #define pow10(x) fmtpow10(x)
40*0d601874SDavid du Colombier
41*0d601874SDavid du Colombier static double
pow10(int n)42*0d601874SDavid du Colombier pow10(int n)
43*0d601874SDavid du Colombier {
44*0d601874SDavid du Colombier double d;
45*0d601874SDavid du Colombier int neg;
46*0d601874SDavid du Colombier
47*0d601874SDavid du Colombier neg = 0;
48*0d601874SDavid du Colombier if(n < 0){
49*0d601874SDavid du Colombier if(n < DBL_MIN_10_EXP){
50*0d601874SDavid du Colombier return 0.;
51*0d601874SDavid du Colombier }
52*0d601874SDavid du Colombier neg = 1;
53*0d601874SDavid du Colombier n = -n;
54*0d601874SDavid du Colombier }else if(n > DBL_MAX_10_EXP){
55*0d601874SDavid du Colombier return HUGE_VAL;
56*0d601874SDavid du Colombier }
57*0d601874SDavid du Colombier if(n < (int)(sizeof(pows10)/sizeof(pows10[0])))
58*0d601874SDavid du Colombier d = pows10[n];
59*0d601874SDavid du Colombier else{
60*0d601874SDavid du Colombier d = pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
61*0d601874SDavid du Colombier for(;;){
62*0d601874SDavid du Colombier n -= sizeof(pows10)/sizeof(pows10[0]) - 1;
63*0d601874SDavid du Colombier if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))){
64*0d601874SDavid du Colombier d *= pows10[n];
65*0d601874SDavid du Colombier break;
66*0d601874SDavid du Colombier }
67*0d601874SDavid du Colombier d *= pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
68*0d601874SDavid du Colombier }
69*0d601874SDavid du Colombier }
70*0d601874SDavid du Colombier if(neg){
71*0d601874SDavid du Colombier return 1./d;
72*0d601874SDavid du Colombier }
73*0d601874SDavid du Colombier return d;
74*0d601874SDavid du Colombier }
75*0d601874SDavid du Colombier
768ccd4a63SDavid du Colombier static int
xadd(char * a,int n,int v)778ccd4a63SDavid du Colombier xadd(char *a, int n, int v)
788ccd4a63SDavid du Colombier {
798ccd4a63SDavid du Colombier char *b;
808ccd4a63SDavid du Colombier int c;
818ccd4a63SDavid du Colombier
828ccd4a63SDavid du Colombier if(n < 0 || n >= NSIGNIF)
838ccd4a63SDavid du Colombier return 0;
848ccd4a63SDavid du Colombier for(b = a+n; b >= a; b--) {
858ccd4a63SDavid du Colombier c = *b + v;
868ccd4a63SDavid du Colombier if(c <= '9') {
878ccd4a63SDavid du Colombier *b = c;
888ccd4a63SDavid du Colombier return 0;
898ccd4a63SDavid du Colombier }
908ccd4a63SDavid du Colombier *b = '0';
918ccd4a63SDavid du Colombier v = 1;
928ccd4a63SDavid du Colombier }
93*0d601874SDavid du Colombier *a = '1'; /* overflow adding */
948ccd4a63SDavid du Colombier return 1;
958ccd4a63SDavid du Colombier }
968ccd4a63SDavid du Colombier
978ccd4a63SDavid du Colombier static int
xsub(char * a,int n,int v)988ccd4a63SDavid du Colombier xsub(char *a, int n, int v)
998ccd4a63SDavid du Colombier {
1008ccd4a63SDavid du Colombier char *b;
1018ccd4a63SDavid du Colombier int c;
1028ccd4a63SDavid du Colombier
1038ccd4a63SDavid du Colombier for(b = a+n; b >= a; b--) {
1048ccd4a63SDavid du Colombier c = *b - v;
1058ccd4a63SDavid du Colombier if(c >= '0') {
1068ccd4a63SDavid du Colombier *b = c;
1078ccd4a63SDavid du Colombier return 0;
1088ccd4a63SDavid du Colombier }
1098ccd4a63SDavid du Colombier *b = '9';
1108ccd4a63SDavid du Colombier v = 1;
1118ccd4a63SDavid du Colombier }
112*0d601874SDavid du Colombier *a = '9'; /* underflow subtracting */
1138ccd4a63SDavid du Colombier return 1;
1148ccd4a63SDavid du Colombier }
1158ccd4a63SDavid du Colombier
1168ccd4a63SDavid du Colombier static void
xdtoa(Fmt * fmt,char * s2,double f)1178ccd4a63SDavid du Colombier xdtoa(Fmt *fmt, char *s2, double f)
1188ccd4a63SDavid du Colombier {
1198ccd4a63SDavid du Colombier char s1[NSIGNIF+10];
1208ccd4a63SDavid du Colombier double g, h;
1218ccd4a63SDavid du Colombier int e, d, i, n;
1228ccd4a63SDavid du Colombier int c1, c2, c3, c4, ucase, sign, chr, prec;
1238ccd4a63SDavid du Colombier
1248ccd4a63SDavid du Colombier prec = FDEFLT;
1258ccd4a63SDavid du Colombier if(fmt->flags & FmtPrec)
1268ccd4a63SDavid du Colombier prec = fmt->prec;
1278ccd4a63SDavid du Colombier if(prec > FDIGIT)
1288ccd4a63SDavid du Colombier prec = FDIGIT;
1298ccd4a63SDavid du Colombier if(__isNaN(f)) {
1308ccd4a63SDavid du Colombier strcpy(s2, "NaN");
1318ccd4a63SDavid du Colombier return;
1328ccd4a63SDavid du Colombier }
1338ccd4a63SDavid du Colombier if(__isInf(f, 1)) {
1348ccd4a63SDavid du Colombier strcpy(s2, "+Inf");
1358ccd4a63SDavid du Colombier return;
1368ccd4a63SDavid du Colombier }
1378ccd4a63SDavid du Colombier if(__isInf(f, -1)) {
1388ccd4a63SDavid du Colombier strcpy(s2, "-Inf");
1398ccd4a63SDavid du Colombier return;
1408ccd4a63SDavid du Colombier }
1418ccd4a63SDavid du Colombier sign = 0;
1428ccd4a63SDavid du Colombier if(f < 0) {
1438ccd4a63SDavid du Colombier f = -f;
1448ccd4a63SDavid du Colombier sign++;
1458ccd4a63SDavid du Colombier }
1468ccd4a63SDavid du Colombier ucase = 0;
1478ccd4a63SDavid du Colombier chr = fmt->r;
1488ccd4a63SDavid du Colombier if(isupper(chr)) {
1498ccd4a63SDavid du Colombier ucase = 1;
1508ccd4a63SDavid du Colombier chr = tolower(chr);
1518ccd4a63SDavid du Colombier }
1528ccd4a63SDavid du Colombier
1538ccd4a63SDavid du Colombier e = 0;
1548ccd4a63SDavid du Colombier g = f;
1558ccd4a63SDavid du Colombier if(g != 0) {
1568ccd4a63SDavid du Colombier frexp(f, &e);
1578ccd4a63SDavid du Colombier e = e * .301029995664;
1588ccd4a63SDavid du Colombier if(e >= -150 && e <= +150) {
1598ccd4a63SDavid du Colombier d = 0;
1608ccd4a63SDavid du Colombier h = f;
1618ccd4a63SDavid du Colombier } else {
1628ccd4a63SDavid du Colombier d = e/2;
1638ccd4a63SDavid du Colombier h = f * pow10(-d);
1648ccd4a63SDavid du Colombier }
1658ccd4a63SDavid du Colombier g = h * pow10(d-e);
1668ccd4a63SDavid du Colombier while(g < 1) {
1678ccd4a63SDavid du Colombier e--;
1688ccd4a63SDavid du Colombier g = h * pow10(d-e);
1698ccd4a63SDavid du Colombier }
1708ccd4a63SDavid du Colombier while(g >= 10) {
1718ccd4a63SDavid du Colombier e++;
1728ccd4a63SDavid du Colombier g = h * pow10(d-e);
1738ccd4a63SDavid du Colombier }
1748ccd4a63SDavid du Colombier }
1758ccd4a63SDavid du Colombier
1768ccd4a63SDavid du Colombier /*
1778ccd4a63SDavid du Colombier * convert NSIGNIF digits and convert
1788ccd4a63SDavid du Colombier * back to get accuracy.
1798ccd4a63SDavid du Colombier */
1808ccd4a63SDavid du Colombier for(i=0; i<NSIGNIF; i++) {
1818ccd4a63SDavid du Colombier d = g;
1828ccd4a63SDavid du Colombier s1[i] = d + '0';
1838ccd4a63SDavid du Colombier g = (g - d) * 10;
1848ccd4a63SDavid du Colombier }
1858ccd4a63SDavid du Colombier s1[i] = 0;
1868ccd4a63SDavid du Colombier
1878ccd4a63SDavid du Colombier /*
1888ccd4a63SDavid du Colombier * try decimal rounding to eliminate 9s
1898ccd4a63SDavid du Colombier */
1908ccd4a63SDavid du Colombier c2 = prec + 1;
1918ccd4a63SDavid du Colombier if(chr == 'f')
1928ccd4a63SDavid du Colombier c2 += e;
1938ccd4a63SDavid du Colombier if(c2 >= NSIGNIF-2) {
1948ccd4a63SDavid du Colombier strcpy(s2, s1);
1958ccd4a63SDavid du Colombier d = e;
1968ccd4a63SDavid du Colombier s1[NSIGNIF-2] = '0';
1978ccd4a63SDavid du Colombier s1[NSIGNIF-1] = '0';
1988ccd4a63SDavid du Colombier sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
1998ccd4a63SDavid du Colombier g = strtod(s1, nil);
2008ccd4a63SDavid du Colombier if(g == f)
2018ccd4a63SDavid du Colombier goto found;
2028ccd4a63SDavid du Colombier if(xadd(s1, NSIGNIF-3, 1)) {
2038ccd4a63SDavid du Colombier e++;
2048ccd4a63SDavid du Colombier sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
2058ccd4a63SDavid du Colombier }
2068ccd4a63SDavid du Colombier g = strtod(s1, nil);
2078ccd4a63SDavid du Colombier if(g == f)
2088ccd4a63SDavid du Colombier goto found;
2098ccd4a63SDavid du Colombier strcpy(s1, s2);
2108ccd4a63SDavid du Colombier e = d;
2118ccd4a63SDavid du Colombier }
2128ccd4a63SDavid du Colombier
2138ccd4a63SDavid du Colombier /*
2148ccd4a63SDavid du Colombier * convert back so s1 gets exact answer
2158ccd4a63SDavid du Colombier */
2168ccd4a63SDavid du Colombier for(;;) {
2178ccd4a63SDavid du Colombier sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
2188ccd4a63SDavid du Colombier g = strtod(s1, nil);
2198ccd4a63SDavid du Colombier if(f > g) {
2208ccd4a63SDavid du Colombier if(xadd(s1, NSIGNIF-1, 1))
2218ccd4a63SDavid du Colombier e--;
2228ccd4a63SDavid du Colombier continue;
2238ccd4a63SDavid du Colombier }
2248ccd4a63SDavid du Colombier if(f < g) {
2258ccd4a63SDavid du Colombier if(xsub(s1, NSIGNIF-1, 1))
2268ccd4a63SDavid du Colombier e++;
2278ccd4a63SDavid du Colombier continue;
2288ccd4a63SDavid du Colombier }
2298ccd4a63SDavid du Colombier break;
2308ccd4a63SDavid du Colombier }
2318ccd4a63SDavid du Colombier
2328ccd4a63SDavid du Colombier found:
2338ccd4a63SDavid du Colombier /*
2348ccd4a63SDavid du Colombier * sign
2358ccd4a63SDavid du Colombier */
2368ccd4a63SDavid du Colombier d = 0;
2378ccd4a63SDavid du Colombier i = 0;
2388ccd4a63SDavid du Colombier if(sign)
2398ccd4a63SDavid du Colombier s2[d++] = '-';
2408ccd4a63SDavid du Colombier else if(fmt->flags & FmtSign)
2418ccd4a63SDavid du Colombier s2[d++] = '+';
2428ccd4a63SDavid du Colombier else if(fmt->flags & FmtSpace)
2438ccd4a63SDavid du Colombier s2[d++] = ' ';
2448ccd4a63SDavid du Colombier
2458ccd4a63SDavid du Colombier /*
2468ccd4a63SDavid du Colombier * copy into final place
2478ccd4a63SDavid du Colombier * c1 digits of leading '0'
2488ccd4a63SDavid du Colombier * c2 digits from conversion
2498ccd4a63SDavid du Colombier * c3 digits of trailing '0'
2508ccd4a63SDavid du Colombier * c4 digits after '.'
2518ccd4a63SDavid du Colombier */
2528ccd4a63SDavid du Colombier c1 = 0;
2538ccd4a63SDavid du Colombier c2 = prec + 1;
2548ccd4a63SDavid du Colombier c3 = 0;
2558ccd4a63SDavid du Colombier c4 = prec;
2568ccd4a63SDavid du Colombier switch(chr) {
2578ccd4a63SDavid du Colombier default:
2588ccd4a63SDavid du Colombier if(xadd(s1, c2, 5))
2598ccd4a63SDavid du Colombier e++;
2608ccd4a63SDavid du Colombier break;
2618ccd4a63SDavid du Colombier case 'g':
2628ccd4a63SDavid du Colombier /*
2638ccd4a63SDavid du Colombier * decide on 'e' of 'f' style convers
2648ccd4a63SDavid du Colombier */
2658ccd4a63SDavid du Colombier if(xadd(s1, c2, 5))
2668ccd4a63SDavid du Colombier e++;
2678ccd4a63SDavid du Colombier if(e >= -5 && e <= prec) {
2688ccd4a63SDavid du Colombier c1 = -e - 1;
2698ccd4a63SDavid du Colombier c4 = prec - e;
2708ccd4a63SDavid du Colombier chr = 'h'; // flag for 'f' style
2718ccd4a63SDavid du Colombier }
2728ccd4a63SDavid du Colombier break;
2738ccd4a63SDavid du Colombier case 'f':
2748ccd4a63SDavid du Colombier if(xadd(s1, c2+e, 5))
2758ccd4a63SDavid du Colombier e++;
2768ccd4a63SDavid du Colombier c1 = -e;
2778ccd4a63SDavid du Colombier if(c1 > prec)
2788ccd4a63SDavid du Colombier c1 = c2;
2798ccd4a63SDavid du Colombier c2 += e;
2808ccd4a63SDavid du Colombier break;
2818ccd4a63SDavid du Colombier }
2828ccd4a63SDavid du Colombier
2838ccd4a63SDavid du Colombier /*
2848ccd4a63SDavid du Colombier * clean up c1 c2 and c3
2858ccd4a63SDavid du Colombier */
2868ccd4a63SDavid du Colombier if(c1 < 0)
2878ccd4a63SDavid du Colombier c1 = 0;
2888ccd4a63SDavid du Colombier if(c2 < 0)
2898ccd4a63SDavid du Colombier c2 = 0;
2908ccd4a63SDavid du Colombier if(c2 > NSIGNIF) {
2918ccd4a63SDavid du Colombier c3 = c2-NSIGNIF;
2928ccd4a63SDavid du Colombier c2 = NSIGNIF;
2938ccd4a63SDavid du Colombier }
2948ccd4a63SDavid du Colombier
2958ccd4a63SDavid du Colombier /*
2968ccd4a63SDavid du Colombier * copy digits
2978ccd4a63SDavid du Colombier */
2988ccd4a63SDavid du Colombier while(c1 > 0) {
2998ccd4a63SDavid du Colombier if(c1+c2+c3 == c4)
3008ccd4a63SDavid du Colombier s2[d++] = '.';
3018ccd4a63SDavid du Colombier s2[d++] = '0';
3028ccd4a63SDavid du Colombier c1--;
3038ccd4a63SDavid du Colombier }
3048ccd4a63SDavid du Colombier while(c2 > 0) {
3058ccd4a63SDavid du Colombier if(c2+c3 == c4)
3068ccd4a63SDavid du Colombier s2[d++] = '.';
3078ccd4a63SDavid du Colombier s2[d++] = s1[i++];
3088ccd4a63SDavid du Colombier c2--;
3098ccd4a63SDavid du Colombier }
3108ccd4a63SDavid du Colombier while(c3 > 0) {
3118ccd4a63SDavid du Colombier if(c3 == c4)
3128ccd4a63SDavid du Colombier s2[d++] = '.';
3138ccd4a63SDavid du Colombier s2[d++] = '0';
3148ccd4a63SDavid du Colombier c3--;
3158ccd4a63SDavid du Colombier }
3168ccd4a63SDavid du Colombier
3178ccd4a63SDavid du Colombier /*
3188ccd4a63SDavid du Colombier * strip trailing '0' on g conv
3198ccd4a63SDavid du Colombier */
3208ccd4a63SDavid du Colombier if(fmt->flags & FmtSharp) {
3218ccd4a63SDavid du Colombier if(0 == c4)
3228ccd4a63SDavid du Colombier s2[d++] = '.';
3238ccd4a63SDavid du Colombier } else
3248ccd4a63SDavid du Colombier if(chr == 'g' || chr == 'h') {
3258ccd4a63SDavid du Colombier for(n=d-1; n>=0; n--)
3268ccd4a63SDavid du Colombier if(s2[n] != '0')
3278ccd4a63SDavid du Colombier break;
3288ccd4a63SDavid du Colombier for(i=n; i>=0; i--)
3298ccd4a63SDavid du Colombier if(s2[i] == '.') {
3308ccd4a63SDavid du Colombier d = n;
3318ccd4a63SDavid du Colombier if(i != n)
3328ccd4a63SDavid du Colombier d++;
3338ccd4a63SDavid du Colombier break;
3348ccd4a63SDavid du Colombier }
3358ccd4a63SDavid du Colombier }
3368ccd4a63SDavid du Colombier if(chr == 'e' || chr == 'g') {
3378ccd4a63SDavid du Colombier if(ucase)
3388ccd4a63SDavid du Colombier s2[d++] = 'E';
3398ccd4a63SDavid du Colombier else
3408ccd4a63SDavid du Colombier s2[d++] = 'e';
3418ccd4a63SDavid du Colombier c1 = e;
3428ccd4a63SDavid du Colombier if(c1 < 0) {
3438ccd4a63SDavid du Colombier s2[d++] = '-';
3448ccd4a63SDavid du Colombier c1 = -c1;
3458ccd4a63SDavid du Colombier } else
3468ccd4a63SDavid du Colombier s2[d++] = '+';
3478ccd4a63SDavid du Colombier if(c1 >= 100) {
3488ccd4a63SDavid du Colombier s2[d++] = c1/100 + '0';
3498ccd4a63SDavid du Colombier c1 = c1%100;
3508ccd4a63SDavid du Colombier }
3518ccd4a63SDavid du Colombier s2[d++] = c1/10 + '0';
3528ccd4a63SDavid du Colombier s2[d++] = c1%10 + '0';
3538ccd4a63SDavid du Colombier }
3548ccd4a63SDavid du Colombier s2[d] = 0;
3558ccd4a63SDavid du Colombier }
3568ccd4a63SDavid du Colombier
357*0d601874SDavid du Colombier static int
floatfmt(Fmt * fmt,double f)358*0d601874SDavid du Colombier floatfmt(Fmt *fmt, double f)
3598ccd4a63SDavid du Colombier {
360*0d601874SDavid du Colombier char s[341]; /* precision+exponent+sign+'.'+null */
3618ccd4a63SDavid du Colombier
3628ccd4a63SDavid du Colombier xdtoa(fmt, s, f);
3638ccd4a63SDavid du Colombier fmt->flags &= FmtWidth|FmtLeft;
364*0d601874SDavid du Colombier __fmtcpy(fmt, s, strlen(s), strlen(s));
3658ccd4a63SDavid du Colombier return 0;
3668ccd4a63SDavid du Colombier }
3678ccd4a63SDavid du Colombier
3688ccd4a63SDavid du Colombier int
__efgfmt(Fmt * f)369*0d601874SDavid du Colombier __efgfmt(Fmt *f)
3708ccd4a63SDavid du Colombier {
3718ccd4a63SDavid du Colombier double d;
3728ccd4a63SDavid du Colombier
3738ccd4a63SDavid du Colombier d = va_arg(f->args, double);
374*0d601874SDavid du Colombier return floatfmt(f, d);
3758ccd4a63SDavid du Colombier }
376