19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <ctype.h>
49a747e4fSDavid du Colombier #include "fmtdef.h"
59a747e4fSDavid du Colombier
69a747e4fSDavid du Colombier enum
79a747e4fSDavid du Colombier {
89a747e4fSDavid du Colombier FDIGIT = 30,
99a747e4fSDavid du Colombier FDEFLT = 6,
109a747e4fSDavid du Colombier NSIGNIF = 17,
11*c4a39de7SDavid du Colombier NEXP10 = 308,
129a747e4fSDavid du Colombier };
139a747e4fSDavid du Colombier
149a747e4fSDavid du Colombier static int
xadd(char * a,int n,int v)159a747e4fSDavid du Colombier xadd(char *a, int n, int v)
169a747e4fSDavid du Colombier {
179a747e4fSDavid du Colombier char *b;
189a747e4fSDavid du Colombier int c;
199a747e4fSDavid du Colombier
209a747e4fSDavid du Colombier if(n < 0 || n >= NSIGNIF)
219a747e4fSDavid du Colombier return 0;
229a747e4fSDavid du Colombier for(b = a+n; b >= a; b--) {
239a747e4fSDavid du Colombier c = *b + v;
249a747e4fSDavid du Colombier if(c <= '9') {
259a747e4fSDavid du Colombier *b = c;
269a747e4fSDavid du Colombier return 0;
279a747e4fSDavid du Colombier }
289a747e4fSDavid du Colombier *b = '0';
299a747e4fSDavid du Colombier v = 1;
309a747e4fSDavid du Colombier }
319a747e4fSDavid du Colombier *a = '1'; // overflow adding
329a747e4fSDavid du Colombier return 1;
339a747e4fSDavid du Colombier }
349a747e4fSDavid du Colombier
359a747e4fSDavid du Colombier static int
xsub(char * a,int n,int v)369a747e4fSDavid du Colombier xsub(char *a, int n, int v)
379a747e4fSDavid du Colombier {
389a747e4fSDavid du Colombier char *b;
399a747e4fSDavid du Colombier int c;
409a747e4fSDavid du Colombier
419a747e4fSDavid du Colombier for(b = a+n; b >= a; b--) {
429a747e4fSDavid du Colombier c = *b - v;
439a747e4fSDavid du Colombier if(c >= '0') {
449a747e4fSDavid du Colombier *b = c;
459a747e4fSDavid du Colombier return 0;
469a747e4fSDavid du Colombier }
479a747e4fSDavid du Colombier *b = '9';
489a747e4fSDavid du Colombier v = 1;
499a747e4fSDavid du Colombier }
509a747e4fSDavid du Colombier *a = '9'; // underflow subtracting
519a747e4fSDavid du Colombier return 1;
529a747e4fSDavid du Colombier }
539a747e4fSDavid du Colombier
549a747e4fSDavid du Colombier static void
xdtoa(Fmt * fmt,char * s2,double f)559a747e4fSDavid du Colombier xdtoa(Fmt *fmt, char *s2, double f)
569a747e4fSDavid du Colombier {
579a747e4fSDavid du Colombier char s1[NSIGNIF+10];
589a747e4fSDavid du Colombier double g, h;
599a747e4fSDavid du Colombier int e, d, i, n;
609a747e4fSDavid du Colombier int c1, c2, c3, c4, ucase, sign, chr, prec;
619a747e4fSDavid du Colombier
629a747e4fSDavid du Colombier prec = FDEFLT;
639a747e4fSDavid du Colombier if(fmt->flags & FmtPrec)
649a747e4fSDavid du Colombier prec = fmt->prec;
659a747e4fSDavid du Colombier if(prec > FDIGIT)
669a747e4fSDavid du Colombier prec = FDIGIT;
679a747e4fSDavid du Colombier if(isNaN(f)) {
689a747e4fSDavid du Colombier strcpy(s2, "NaN");
699a747e4fSDavid du Colombier return;
709a747e4fSDavid du Colombier }
719a747e4fSDavid du Colombier if(isInf(f, 1)) {
729a747e4fSDavid du Colombier strcpy(s2, "+Inf");
739a747e4fSDavid du Colombier return;
749a747e4fSDavid du Colombier }
759a747e4fSDavid du Colombier if(isInf(f, -1)) {
769a747e4fSDavid du Colombier strcpy(s2, "-Inf");
779a747e4fSDavid du Colombier return;
789a747e4fSDavid du Colombier }
799a747e4fSDavid du Colombier sign = 0;
809a747e4fSDavid du Colombier if(f < 0) {
819a747e4fSDavid du Colombier f = -f;
829a747e4fSDavid du Colombier sign++;
839a747e4fSDavid du Colombier }
849a747e4fSDavid du Colombier ucase = 0;
859a747e4fSDavid du Colombier chr = fmt->r;
869a747e4fSDavid du Colombier if(isupper(chr)) {
879a747e4fSDavid du Colombier ucase = 1;
889a747e4fSDavid du Colombier chr = tolower(chr);
899a747e4fSDavid du Colombier }
909a747e4fSDavid du Colombier
919a747e4fSDavid du Colombier e = 0;
929a747e4fSDavid du Colombier g = f;
939a747e4fSDavid du Colombier if(g != 0) {
949a747e4fSDavid du Colombier frexp(f, &e);
959a747e4fSDavid du Colombier e = e * .301029995664;
969a747e4fSDavid du Colombier if(e >= -150 && e <= +150) {
979a747e4fSDavid du Colombier d = 0;
989a747e4fSDavid du Colombier h = f;
999a747e4fSDavid du Colombier } else {
1009a747e4fSDavid du Colombier d = e/2;
1019a747e4fSDavid du Colombier h = f * pow10(-d);
1029a747e4fSDavid du Colombier }
1039a747e4fSDavid du Colombier g = h * pow10(d-e);
1049a747e4fSDavid du Colombier while(g < 1) {
1059a747e4fSDavid du Colombier e--;
1069a747e4fSDavid du Colombier g = h * pow10(d-e);
1079a747e4fSDavid du Colombier }
1089a747e4fSDavid du Colombier while(g >= 10) {
1099a747e4fSDavid du Colombier e++;
1109a747e4fSDavid du Colombier g = h * pow10(d-e);
1119a747e4fSDavid du Colombier }
1129a747e4fSDavid du Colombier }
1139a747e4fSDavid du Colombier
1149a747e4fSDavid du Colombier /*
1159a747e4fSDavid du Colombier * convert NSIGNIF digits and convert
1169a747e4fSDavid du Colombier * back to get accuracy.
1179a747e4fSDavid du Colombier */
1189a747e4fSDavid du Colombier for(i=0; i<NSIGNIF; i++) {
1199a747e4fSDavid du Colombier d = g;
1209a747e4fSDavid du Colombier s1[i] = d + '0';
1219a747e4fSDavid du Colombier g = (g - d) * 10;
1229a747e4fSDavid du Colombier }
1239a747e4fSDavid du Colombier s1[i] = 0;
1249a747e4fSDavid du Colombier
1259a747e4fSDavid du Colombier /*
1269a747e4fSDavid du Colombier * try decimal rounding to eliminate 9s
1279a747e4fSDavid du Colombier */
1289a747e4fSDavid du Colombier c2 = prec + 1;
1299a747e4fSDavid du Colombier if(chr == 'f')
1309a747e4fSDavid du Colombier c2 += e;
1319a747e4fSDavid du Colombier if(c2 >= NSIGNIF-2) {
1329a747e4fSDavid du Colombier strcpy(s2, s1);
1339a747e4fSDavid du Colombier d = e;
1349a747e4fSDavid du Colombier s1[NSIGNIF-2] = '0';
1359a747e4fSDavid du Colombier s1[NSIGNIF-1] = '0';
1369a747e4fSDavid du Colombier sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
1379a747e4fSDavid du Colombier g = strtod(s1, nil);
1389a747e4fSDavid du Colombier if(g == f)
1399a747e4fSDavid du Colombier goto found;
1409a747e4fSDavid du Colombier if(xadd(s1, NSIGNIF-3, 1)) {
1419a747e4fSDavid du Colombier e++;
1429a747e4fSDavid du Colombier sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
1439a747e4fSDavid du Colombier }
1449a747e4fSDavid du Colombier g = strtod(s1, nil);
1459a747e4fSDavid du Colombier if(g == f)
1469a747e4fSDavid du Colombier goto found;
1479a747e4fSDavid du Colombier strcpy(s1, s2);
1489a747e4fSDavid du Colombier e = d;
1499a747e4fSDavid du Colombier }
1509a747e4fSDavid du Colombier
1519a747e4fSDavid du Colombier /*
1529a747e4fSDavid du Colombier * convert back so s1 gets exact answer
1539a747e4fSDavid du Colombier */
1549a747e4fSDavid du Colombier for(;;) {
1559a747e4fSDavid du Colombier sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
1569a747e4fSDavid du Colombier g = strtod(s1, nil);
1579a747e4fSDavid du Colombier if(f > g) {
1589a747e4fSDavid du Colombier if(xadd(s1, NSIGNIF-1, 1))
1599a747e4fSDavid du Colombier e--;
1609a747e4fSDavid du Colombier continue;
1619a747e4fSDavid du Colombier }
1629a747e4fSDavid du Colombier if(f < g) {
1639a747e4fSDavid du Colombier if(xsub(s1, NSIGNIF-1, 1))
1649a747e4fSDavid du Colombier e++;
1659a747e4fSDavid du Colombier continue;
1669a747e4fSDavid du Colombier }
1679a747e4fSDavid du Colombier break;
1689a747e4fSDavid du Colombier }
1699a747e4fSDavid du Colombier
1709a747e4fSDavid du Colombier found:
1719a747e4fSDavid du Colombier /*
1729a747e4fSDavid du Colombier * sign
1739a747e4fSDavid du Colombier */
1749a747e4fSDavid du Colombier d = 0;
1759a747e4fSDavid du Colombier i = 0;
1769a747e4fSDavid du Colombier if(sign)
1779a747e4fSDavid du Colombier s2[d++] = '-';
1789a747e4fSDavid du Colombier else if(fmt->flags & FmtSign)
1799a747e4fSDavid du Colombier s2[d++] = '+';
1809a747e4fSDavid du Colombier else if(fmt->flags & FmtSpace)
181d9306527SDavid du Colombier s2[d++] = ' ';
1829a747e4fSDavid du Colombier
1839a747e4fSDavid du Colombier /*
1849a747e4fSDavid du Colombier * copy into final place
1859a747e4fSDavid du Colombier * c1 digits of leading '0'
1869a747e4fSDavid du Colombier * c2 digits from conversion
1879a747e4fSDavid du Colombier * c3 digits of trailing '0'
1889a747e4fSDavid du Colombier * c4 digits after '.'
1899a747e4fSDavid du Colombier */
1909a747e4fSDavid du Colombier c1 = 0;
1919a747e4fSDavid du Colombier c2 = prec + 1;
1929a747e4fSDavid du Colombier c3 = 0;
1939a747e4fSDavid du Colombier c4 = prec;
1949a747e4fSDavid du Colombier switch(chr) {
1959a747e4fSDavid du Colombier default:
1969a747e4fSDavid du Colombier if(xadd(s1, c2, 5))
1979a747e4fSDavid du Colombier e++;
1989a747e4fSDavid du Colombier break;
1999a747e4fSDavid du Colombier case 'g':
2009a747e4fSDavid du Colombier /*
2019a747e4fSDavid du Colombier * decide on 'e' of 'f' style convers
2029a747e4fSDavid du Colombier */
2039a747e4fSDavid du Colombier if(xadd(s1, c2, 5))
2049a747e4fSDavid du Colombier e++;
2059a747e4fSDavid du Colombier if(e >= -5 && e <= prec) {
2069a747e4fSDavid du Colombier c1 = -e - 1;
2079a747e4fSDavid du Colombier c4 = prec - e;
2089a747e4fSDavid du Colombier chr = 'h'; // flag for 'f' style
2099a747e4fSDavid du Colombier }
2109a747e4fSDavid du Colombier break;
2119a747e4fSDavid du Colombier case 'f':
2129a747e4fSDavid du Colombier if(xadd(s1, c2+e, 5))
2139a747e4fSDavid du Colombier e++;
2149a747e4fSDavid du Colombier c1 = -e;
2159a747e4fSDavid du Colombier if(c1 > prec)
2169a747e4fSDavid du Colombier c1 = c2;
2179a747e4fSDavid du Colombier c2 += e;
2189a747e4fSDavid du Colombier break;
2199a747e4fSDavid du Colombier }
2209a747e4fSDavid du Colombier
2219a747e4fSDavid du Colombier /*
2229a747e4fSDavid du Colombier * clean up c1 c2 and c3
2239a747e4fSDavid du Colombier */
2249a747e4fSDavid du Colombier if(c1 < 0)
2259a747e4fSDavid du Colombier c1 = 0;
2269a747e4fSDavid du Colombier if(c2 < 0)
2279a747e4fSDavid du Colombier c2 = 0;
2289a747e4fSDavid du Colombier if(c2 > NSIGNIF) {
2299a747e4fSDavid du Colombier c3 = c2-NSIGNIF;
2309a747e4fSDavid du Colombier c2 = NSIGNIF;
2319a747e4fSDavid du Colombier }
2329a747e4fSDavid du Colombier
2339a747e4fSDavid du Colombier /*
2349a747e4fSDavid du Colombier * copy digits
2359a747e4fSDavid du Colombier */
2369a747e4fSDavid du Colombier while(c1 > 0) {
2379a747e4fSDavid du Colombier if(c1+c2+c3 == c4)
2389a747e4fSDavid du Colombier s2[d++] = '.';
2399a747e4fSDavid du Colombier s2[d++] = '0';
2409a747e4fSDavid du Colombier c1--;
2419a747e4fSDavid du Colombier }
2429a747e4fSDavid du Colombier while(c2 > 0) {
2439a747e4fSDavid du Colombier if(c2+c3 == c4)
2449a747e4fSDavid du Colombier s2[d++] = '.';
2459a747e4fSDavid du Colombier s2[d++] = s1[i++];
2469a747e4fSDavid du Colombier c2--;
2479a747e4fSDavid du Colombier }
2489a747e4fSDavid du Colombier while(c3 > 0) {
2499a747e4fSDavid du Colombier if(c3 == c4)
2509a747e4fSDavid du Colombier s2[d++] = '.';
2519a747e4fSDavid du Colombier s2[d++] = '0';
2529a747e4fSDavid du Colombier c3--;
2539a747e4fSDavid du Colombier }
2549a747e4fSDavid du Colombier
2559a747e4fSDavid du Colombier /*
2569a747e4fSDavid du Colombier * strip trailing '0' on g conv
2579a747e4fSDavid du Colombier */
2589a747e4fSDavid du Colombier if(fmt->flags & FmtSharp) {
2599a747e4fSDavid du Colombier if(0 == c4)
2609a747e4fSDavid du Colombier s2[d++] = '.';
2619a747e4fSDavid du Colombier } else
2629a747e4fSDavid du Colombier if(chr == 'g' || chr == 'h') {
2639a747e4fSDavid du Colombier for(n=d-1; n>=0; n--)
2649a747e4fSDavid du Colombier if(s2[n] != '0')
2659a747e4fSDavid du Colombier break;
2669a747e4fSDavid du Colombier for(i=n; i>=0; i--)
2679a747e4fSDavid du Colombier if(s2[i] == '.') {
2689a747e4fSDavid du Colombier d = n;
2699a747e4fSDavid du Colombier if(i != n)
2709a747e4fSDavid du Colombier d++;
2719a747e4fSDavid du Colombier break;
2729a747e4fSDavid du Colombier }
2739a747e4fSDavid du Colombier }
2749a747e4fSDavid du Colombier if(chr == 'e' || chr == 'g') {
2759a747e4fSDavid du Colombier if(ucase)
2769a747e4fSDavid du Colombier s2[d++] = 'E';
2779a747e4fSDavid du Colombier else
2789a747e4fSDavid du Colombier s2[d++] = 'e';
2799a747e4fSDavid du Colombier c1 = e;
2809a747e4fSDavid du Colombier if(c1 < 0) {
2819a747e4fSDavid du Colombier s2[d++] = '-';
2829a747e4fSDavid du Colombier c1 = -c1;
2839a747e4fSDavid du Colombier } else
2849a747e4fSDavid du Colombier s2[d++] = '+';
2859a747e4fSDavid du Colombier if(c1 >= 100) {
2869a747e4fSDavid du Colombier s2[d++] = c1/100 + '0';
2879a747e4fSDavid du Colombier c1 = c1%100;
2889a747e4fSDavid du Colombier }
2899a747e4fSDavid du Colombier s2[d++] = c1/10 + '0';
2909a747e4fSDavid du Colombier s2[d++] = c1%10 + '0';
2919a747e4fSDavid du Colombier }
2929a747e4fSDavid du Colombier s2[d] = 0;
2939a747e4fSDavid du Colombier }
2949a747e4fSDavid du Colombier
2959a747e4fSDavid du Colombier int
_floatfmt(Fmt * fmt,double f)2969a747e4fSDavid du Colombier _floatfmt(Fmt *fmt, double f)
2979a747e4fSDavid du Colombier {
298*c4a39de7SDavid du Colombier char s[1+NEXP10+1+FDIGIT+1];
2999a747e4fSDavid du Colombier
300*c4a39de7SDavid du Colombier /*
301*c4a39de7SDavid du Colombier * The max length of a %f string is
302*c4a39de7SDavid du Colombier * '[+-]'+"max exponent"+'.'+"max precision"+'\0'
303*c4a39de7SDavid du Colombier * which is 341 currently.
304*c4a39de7SDavid du Colombier */
3059a747e4fSDavid du Colombier xdtoa(fmt, s, f);
3069a747e4fSDavid du Colombier fmt->flags &= FmtWidth|FmtLeft;
3079a747e4fSDavid du Colombier _fmtcpy(fmt, s, strlen(s), strlen(s));
3089a747e4fSDavid du Colombier return 0;
3099a747e4fSDavid du Colombier }
3109a747e4fSDavid du Colombier
3119a747e4fSDavid du Colombier int
_efgfmt(Fmt * f)3129a747e4fSDavid du Colombier _efgfmt(Fmt *f)
3139a747e4fSDavid du Colombier {
3149a747e4fSDavid du Colombier double d;
3159a747e4fSDavid du Colombier
3169a747e4fSDavid du Colombier d = va_arg(f->args, double);
3179a747e4fSDavid du Colombier return _floatfmt(f, d);
3189a747e4fSDavid du Colombier }
319