xref: /plan9/sys/src/libc/port/strtod.c (revision b85a83648eec38fe82b6f00adfd7828ceec5ee8d)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
359cc4ca5SDavid du Colombier #include <ctype.h>
43e12c5d1SDavid du Colombier 
559cc4ca5SDavid du Colombier /*
659cc4ca5SDavid du Colombier  * This routine will convert to arbitrary precision
759cc4ca5SDavid du Colombier  * floating point entirely in multi-precision fixed.
859cc4ca5SDavid du Colombier  * The answer is the closest floating point number to
959cc4ca5SDavid du Colombier  * the given decimal number. Exactly half way are
1059cc4ca5SDavid du Colombier  * rounded ala ieee rules.
1159cc4ca5SDavid du Colombier  * Method is to scale input decimal between .500 and .999...
1259cc4ca5SDavid du Colombier  * with external power of 2, then binary search for the
1359cc4ca5SDavid du Colombier  * closest mantissa to this decimal number.
1459cc4ca5SDavid du Colombier  * Nmant is is the required precision. (53 for ieee dp)
1559cc4ca5SDavid du Colombier  * Nbits is the max number of bits/word. (must be <= 28)
1659cc4ca5SDavid du Colombier  * Prec is calculated - the number of words of fixed mantissa.
1759cc4ca5SDavid du Colombier  */
1859cc4ca5SDavid du Colombier enum
193e12c5d1SDavid du Colombier {
2059cc4ca5SDavid du Colombier 	Nbits	= 28,				// bits safely represented in a ulong
2159cc4ca5SDavid du Colombier 	Nmant	= 53,				// bits of precision required
22*9a747e4fSDavid du Colombier 	Bias		= 1022,
2359cc4ca5SDavid du Colombier 	Prec	= (Nmant+Nbits+1)/Nbits,	// words of Nbits each to represent mantissa
2459cc4ca5SDavid du Colombier 	Sigbit	= 1<<(Prec*Nbits-Nmant),	// first significant bit of Prec-th word
2559cc4ca5SDavid du Colombier 	Ndig	= 1500,
2659cc4ca5SDavid du Colombier 	One	= (ulong)(1<<Nbits),
2759cc4ca5SDavid du Colombier 	Half	= (ulong)(One>>1),
2859cc4ca5SDavid du Colombier 	Maxe	= 310,
2959cc4ca5SDavid du Colombier 	Fsign	= 1<<0,		// found -
3059cc4ca5SDavid du Colombier 	Fesign	= 1<<1,		// found e-
3159cc4ca5SDavid du Colombier 	Fdpoint	= 1<<2,		// found .
3259cc4ca5SDavid du Colombier 
3359cc4ca5SDavid du Colombier 	S0	= 0,		// _		_S0	+S1	#S2	.S3
3459cc4ca5SDavid du Colombier 	S1,			// _+		#S2	.S3
3559cc4ca5SDavid du Colombier 	S2,			// _+#		#S2	.S4	eS5
3659cc4ca5SDavid du Colombier 	S3,			// _+.		#S4
3759cc4ca5SDavid du Colombier 	S4,			// _+#.#	#S4	eS5
3859cc4ca5SDavid du Colombier 	S5,			// _+#.#e	+S6	#S7
3959cc4ca5SDavid du Colombier 	S6,			// _+#.#e+	#S7
4059cc4ca5SDavid du Colombier 	S7,			// _+#.#e+#	#S7
4159cc4ca5SDavid du Colombier };
4259cc4ca5SDavid du Colombier 
4359cc4ca5SDavid du Colombier static	int	xcmp(char*, char*);
4459cc4ca5SDavid du Colombier static	int	fpcmp(char*, ulong*);
4559cc4ca5SDavid du Colombier static	void	frnorm(ulong*);
4659cc4ca5SDavid du Colombier static	void	divascii(char*, int*, int*, int*);
4759cc4ca5SDavid du Colombier static	void	mulascii(char*, int*, int*, int*);
48*9a747e4fSDavid du Colombier static	void	divby(char*, int*, int);
4959cc4ca5SDavid du Colombier 
5059cc4ca5SDavid du Colombier typedef	struct	Tab	Tab;
5159cc4ca5SDavid du Colombier struct	Tab
5259cc4ca5SDavid du Colombier {
5359cc4ca5SDavid du Colombier 	int	bp;
5459cc4ca5SDavid du Colombier 	int	siz;
5559cc4ca5SDavid du Colombier 	char*	cmp;
5659cc4ca5SDavid du Colombier };
573e12c5d1SDavid du Colombier 
583e12c5d1SDavid du Colombier double
strtod(char * as,char ** aas)5959cc4ca5SDavid du Colombier strtod(char *as, char **aas)
603e12c5d1SDavid du Colombier {
61*9a747e4fSDavid du Colombier 	int na, ona, ex, dp, bp, c, i, flag, state;
62*9a747e4fSDavid du Colombier 	ulong low[Prec], hig[Prec], mid[Prec], num, den;
633e12c5d1SDavid du Colombier 	double d;
6459cc4ca5SDavid du Colombier 	char *s, a[Ndig];
653e12c5d1SDavid du Colombier 
6659cc4ca5SDavid du Colombier 	flag = 0;	// Fsign, Fesign, Fdpoint
6759cc4ca5SDavid du Colombier 	na = 0;		// number of digits of a[]
6859cc4ca5SDavid du Colombier 	dp = 0;		// na of decimal point
6959cc4ca5SDavid du Colombier 	ex = 0;		// exonent
7059cc4ca5SDavid du Colombier 
7159cc4ca5SDavid du Colombier 	state = S0;
7259cc4ca5SDavid du Colombier 	for(s=as;; s++) {
7359cc4ca5SDavid du Colombier 		c = *s;
7459cc4ca5SDavid du Colombier 		if(c >= '0' && c <= '9') {
7559cc4ca5SDavid du Colombier 			switch(state) {
7659cc4ca5SDavid du Colombier 			case S0:
7759cc4ca5SDavid du Colombier 			case S1:
7859cc4ca5SDavid du Colombier 			case S2:
7959cc4ca5SDavid du Colombier 				state = S2;
803e12c5d1SDavid du Colombier 				break;
8159cc4ca5SDavid du Colombier 			case S3:
8259cc4ca5SDavid du Colombier 			case S4:
8359cc4ca5SDavid du Colombier 				state = S4;
8459cc4ca5SDavid du Colombier 				break;
8559cc4ca5SDavid du Colombier 
8659cc4ca5SDavid du Colombier 			case S5:
8759cc4ca5SDavid du Colombier 			case S6:
8859cc4ca5SDavid du Colombier 			case S7:
8959cc4ca5SDavid du Colombier 				state = S7;
9059cc4ca5SDavid du Colombier 				ex = ex*10 + (c-'0');
9159cc4ca5SDavid du Colombier 				continue;
923e12c5d1SDavid du Colombier 			}
9359cc4ca5SDavid du Colombier 			if(na == 0 && c == '0') {
9459cc4ca5SDavid du Colombier 				dp--;
9559cc4ca5SDavid du Colombier 				continue;
963e12c5d1SDavid du Colombier 			}
9759cc4ca5SDavid du Colombier 			if(na < Ndig-50)
9859cc4ca5SDavid du Colombier 				a[na++] = c;
9959cc4ca5SDavid du Colombier 			continue;
10059cc4ca5SDavid du Colombier 		}
10159cc4ca5SDavid du Colombier 		switch(c) {
10259cc4ca5SDavid du Colombier 		case '\t':
10359cc4ca5SDavid du Colombier 		case '\n':
10459cc4ca5SDavid du Colombier 		case '\v':
10559cc4ca5SDavid du Colombier 		case '\f':
10659cc4ca5SDavid du Colombier 		case '\r':
10759cc4ca5SDavid du Colombier 		case ' ':
10859cc4ca5SDavid du Colombier 			if(state == S0)
10959cc4ca5SDavid du Colombier 				continue;
11059cc4ca5SDavid du Colombier 			break;
11159cc4ca5SDavid du Colombier 		case '-':
11259cc4ca5SDavid du Colombier 			if(state == S0)
11359cc4ca5SDavid du Colombier 				flag |= Fsign;
11459cc4ca5SDavid du Colombier 			else
11559cc4ca5SDavid du Colombier 				flag |= Fesign;
11659cc4ca5SDavid du Colombier 		case '+':
11759cc4ca5SDavid du Colombier 			if(state == S0)
11859cc4ca5SDavid du Colombier 				state = S1;
11959cc4ca5SDavid du Colombier 			else
12059cc4ca5SDavid du Colombier 			if(state == S5)
12159cc4ca5SDavid du Colombier 				state = S6;
12259cc4ca5SDavid du Colombier 			else
12359cc4ca5SDavid du Colombier 				break;	// syntax
12459cc4ca5SDavid du Colombier 			continue;
12559cc4ca5SDavid du Colombier 		case '.':
12659cc4ca5SDavid du Colombier 			flag |= Fdpoint;
12759cc4ca5SDavid du Colombier 			dp = na;
12859cc4ca5SDavid du Colombier 			if(state == S0 || state == S1) {
12959cc4ca5SDavid du Colombier 				state = S3;
13059cc4ca5SDavid du Colombier 				continue;
13159cc4ca5SDavid du Colombier 			}
13259cc4ca5SDavid du Colombier 			if(state == S2) {
13359cc4ca5SDavid du Colombier 				state = S4;
13459cc4ca5SDavid du Colombier 				continue;
13559cc4ca5SDavid du Colombier 			}
13659cc4ca5SDavid du Colombier 			break;
13759cc4ca5SDavid du Colombier 		case 'e':
13859cc4ca5SDavid du Colombier 		case 'E':
13959cc4ca5SDavid du Colombier 			if(state == S2 || state == S4) {
14059cc4ca5SDavid du Colombier 				state = S5;
14159cc4ca5SDavid du Colombier 				continue;
14259cc4ca5SDavid du Colombier 			}
14359cc4ca5SDavid du Colombier 			break;
14459cc4ca5SDavid du Colombier 		}
14559cc4ca5SDavid du Colombier 		break;
14659cc4ca5SDavid du Colombier 	}
14759cc4ca5SDavid du Colombier 
14859cc4ca5SDavid du Colombier 	/*
14959cc4ca5SDavid du Colombier 	 * clean up return char-pointer
15059cc4ca5SDavid du Colombier 	 */
15159cc4ca5SDavid du Colombier 	switch(state) {
15259cc4ca5SDavid du Colombier 	case S0:
15359cc4ca5SDavid du Colombier 		if(xcmp(s, "nan") == 0) {
15459cc4ca5SDavid du Colombier 			if(aas != nil)
15559cc4ca5SDavid du Colombier 				*aas = s+3;
15659cc4ca5SDavid du Colombier 			goto retnan;
15759cc4ca5SDavid du Colombier 		}
15859cc4ca5SDavid du Colombier 	case S1:
15959cc4ca5SDavid du Colombier 		if(xcmp(s, "infinity") == 0) {
16059cc4ca5SDavid du Colombier 			if(aas != nil)
16159cc4ca5SDavid du Colombier 				*aas = s+8;
16259cc4ca5SDavid du Colombier 			goto retinf;
16359cc4ca5SDavid du Colombier 		}
16459cc4ca5SDavid du Colombier 		if(xcmp(s, "inf") == 0) {
16559cc4ca5SDavid du Colombier 			if(aas != nil)
16659cc4ca5SDavid du Colombier 				*aas = s+3;
16759cc4ca5SDavid du Colombier 			goto retinf;
16859cc4ca5SDavid du Colombier 		}
16959cc4ca5SDavid du Colombier 	case S3:
17059cc4ca5SDavid du Colombier 		if(aas != nil)
17159cc4ca5SDavid du Colombier 			*aas = as;
17259cc4ca5SDavid du Colombier 		goto ret0;	// no digits found
17359cc4ca5SDavid du Colombier 	case S6:
17459cc4ca5SDavid du Colombier 		s--;		// back over +-
17559cc4ca5SDavid du Colombier 	case S5:
17659cc4ca5SDavid du Colombier 		s--;		// back over e
17759cc4ca5SDavid du Colombier 		break;
17859cc4ca5SDavid du Colombier 	}
17959cc4ca5SDavid du Colombier 	if(aas != nil)
18059cc4ca5SDavid du Colombier 		*aas = s;
18159cc4ca5SDavid du Colombier 
18259cc4ca5SDavid du Colombier 	if(flag & Fdpoint)
18359cc4ca5SDavid du Colombier 	while(na > 0 && a[na-1] == '0')
18459cc4ca5SDavid du Colombier 		na--;
18559cc4ca5SDavid du Colombier 	if(na == 0)
18659cc4ca5SDavid du Colombier 		goto ret0;	// zero
18759cc4ca5SDavid du Colombier 	a[na] = 0;
18859cc4ca5SDavid du Colombier 	if(!(flag & Fdpoint))
18959cc4ca5SDavid du Colombier 		dp = na;
19059cc4ca5SDavid du Colombier 	if(flag & Fesign)
19159cc4ca5SDavid du Colombier 		ex = -ex;
19259cc4ca5SDavid du Colombier 	dp += ex;
193*9a747e4fSDavid du Colombier 	if(dp < -Maxe-Nmant/3)	/* actually -Nmant*log(2)/log(10), but Nmant/3 close enough */
19459cc4ca5SDavid du Colombier 		goto ret0;	// underflow by exp
19559cc4ca5SDavid du Colombier 	else
19659cc4ca5SDavid du Colombier 	if(dp > +Maxe)
19759cc4ca5SDavid du Colombier 		goto retinf;	// overflow by exp
19859cc4ca5SDavid du Colombier 
19959cc4ca5SDavid du Colombier 	/*
20059cc4ca5SDavid du Colombier 	 * normalize the decimal ascii number
20159cc4ca5SDavid du Colombier 	 * to range .[5-9][0-9]* e0
20259cc4ca5SDavid du Colombier 	 */
20359cc4ca5SDavid du Colombier 	bp = 0;		// binary exponent
20459cc4ca5SDavid du Colombier 	while(dp > 0)
20559cc4ca5SDavid du Colombier 		divascii(a, &na, &dp, &bp);
20659cc4ca5SDavid du Colombier 	while(dp < 0 || a[0] < '5')
20759cc4ca5SDavid du Colombier 		mulascii(a, &na, &dp, &bp);
208*9a747e4fSDavid du Colombier 	a[na] = 0;
209*9a747e4fSDavid du Colombier 
210*9a747e4fSDavid du Colombier 	/*
211*9a747e4fSDavid du Colombier 	 * very small numbers are represented using
212*9a747e4fSDavid du Colombier 	 * bp = -Bias+1.  adjust accordingly.
213*9a747e4fSDavid du Colombier 	 */
214*9a747e4fSDavid du Colombier 	if(bp < -Bias+1){
215*9a747e4fSDavid du Colombier 		ona = na;
216*9a747e4fSDavid du Colombier 		divby(a, &na, -bp-Bias+1);
217*9a747e4fSDavid du Colombier 		if(na < ona){
218*9a747e4fSDavid du Colombier 			memmove(a+ona-na, a, na);
219*9a747e4fSDavid du Colombier 			memset(a, '0', ona-na);
220*9a747e4fSDavid du Colombier 			na = ona;
221*9a747e4fSDavid du Colombier 		}
222*9a747e4fSDavid du Colombier 		a[na] = 0;
223*9a747e4fSDavid du Colombier 		bp = -Bias+1;
224*9a747e4fSDavid du Colombier 	}
22559cc4ca5SDavid du Colombier 
22659cc4ca5SDavid du Colombier 	/* close approx by naive conversion */
227*9a747e4fSDavid du Colombier 	num = 0;
228*9a747e4fSDavid du Colombier 	den = 1;
229*9a747e4fSDavid du Colombier 	for(i=0; i<9 && (c=a[i]); i++) {
230*9a747e4fSDavid du Colombier 		num = num*10 + (c-'0');
231*9a747e4fSDavid du Colombier 		den *= 10;
23259cc4ca5SDavid du Colombier 	}
233*9a747e4fSDavid du Colombier 	low[0] = umuldiv(num, One, den);
234*9a747e4fSDavid du Colombier 	hig[0] = umuldiv(num+1, One, den);
23559cc4ca5SDavid du Colombier 	for(i=1; i<Prec; i++) {
23659cc4ca5SDavid du Colombier 		low[i] = 0;
23759cc4ca5SDavid du Colombier 		hig[i] = One-1;
23859cc4ca5SDavid du Colombier 	}
23959cc4ca5SDavid du Colombier 
24059cc4ca5SDavid du Colombier 	/* binary search for closest mantissa */
24159cc4ca5SDavid du Colombier 	for(;;) {
24259cc4ca5SDavid du Colombier 		/* mid = (hig + low) / 2 */
24359cc4ca5SDavid du Colombier 		c = 0;
24459cc4ca5SDavid du Colombier 		for(i=0; i<Prec; i++) {
24559cc4ca5SDavid du Colombier 			mid[i] = hig[i] + low[i];
24659cc4ca5SDavid du Colombier 			if(c)
24759cc4ca5SDavid du Colombier 				mid[i] += One;
24859cc4ca5SDavid du Colombier 			c = mid[i] & 1;
24959cc4ca5SDavid du Colombier 			mid[i] >>= 1;
25059cc4ca5SDavid du Colombier 		}
25159cc4ca5SDavid du Colombier 		frnorm(mid);
25259cc4ca5SDavid du Colombier 
25359cc4ca5SDavid du Colombier 		/* compare */
25459cc4ca5SDavid du Colombier 		c = fpcmp(a, mid);
25559cc4ca5SDavid du Colombier 		if(c > 0) {
25659cc4ca5SDavid du Colombier 			c = 1;
25759cc4ca5SDavid du Colombier 			for(i=0; i<Prec; i++)
25859cc4ca5SDavid du Colombier 				if(low[i] != mid[i]) {
25959cc4ca5SDavid du Colombier 					c = 0;
26059cc4ca5SDavid du Colombier 					low[i] = mid[i];
26159cc4ca5SDavid du Colombier 				}
26259cc4ca5SDavid du Colombier 			if(c)
26359cc4ca5SDavid du Colombier 				break;	// between mid and hig
26459cc4ca5SDavid du Colombier 			continue;
26559cc4ca5SDavid du Colombier 		}
26659cc4ca5SDavid du Colombier 		if(c < 0) {
26759cc4ca5SDavid du Colombier 			for(i=0; i<Prec; i++)
26859cc4ca5SDavid du Colombier 				hig[i] = mid[i];
26959cc4ca5SDavid du Colombier 			continue;
27059cc4ca5SDavid du Colombier 		}
27159cc4ca5SDavid du Colombier 
27259cc4ca5SDavid du Colombier 		/* only hard part is if even/odd roundings wants to go up */
27359cc4ca5SDavid du Colombier 		c = mid[Prec-1] & (Sigbit-1);
27459cc4ca5SDavid du Colombier 		if(c == Sigbit/2 && (mid[Prec-1]&Sigbit) == 0)
27559cc4ca5SDavid du Colombier 			mid[Prec-1] -= c;
27659cc4ca5SDavid du Colombier 		break;	// exactly mid
27759cc4ca5SDavid du Colombier 	}
27859cc4ca5SDavid du Colombier 
27959cc4ca5SDavid du Colombier 	/* normal rounding applies */
28059cc4ca5SDavid du Colombier 	c = mid[Prec-1] & (Sigbit-1);
28159cc4ca5SDavid du Colombier 	mid[Prec-1] -= c;
28259cc4ca5SDavid du Colombier 	if(c >= Sigbit/2) {
28359cc4ca5SDavid du Colombier 		mid[Prec-1] += Sigbit;
28459cc4ca5SDavid du Colombier 		frnorm(mid);
28559cc4ca5SDavid du Colombier 	}
286*9a747e4fSDavid du Colombier 	d = 0;
287*9a747e4fSDavid du Colombier 	for(i=0; i<Prec; i++)
288*9a747e4fSDavid du Colombier 		d = d*One + mid[i];
289*9a747e4fSDavid du Colombier 	if(flag & Fsign)
290*9a747e4fSDavid du Colombier 		d = -d;
291*9a747e4fSDavid du Colombier 	d = ldexp(d, bp - Prec*Nbits);
292*9a747e4fSDavid du Colombier 	return d;
29359cc4ca5SDavid du Colombier 
29459cc4ca5SDavid du Colombier ret0:
29559cc4ca5SDavid du Colombier 	return 0;
29659cc4ca5SDavid du Colombier 
29759cc4ca5SDavid du Colombier retnan:
29859cc4ca5SDavid du Colombier 	return NaN();
29959cc4ca5SDavid du Colombier 
30059cc4ca5SDavid du Colombier retinf:
30159cc4ca5SDavid du Colombier 	if(flag & Fsign)
30259cc4ca5SDavid du Colombier 		return Inf(-1);
30359cc4ca5SDavid du Colombier 	return Inf(+1);
3043e12c5d1SDavid du Colombier }
30559cc4ca5SDavid du Colombier 
30659cc4ca5SDavid du Colombier static void
frnorm(ulong * f)30759cc4ca5SDavid du Colombier frnorm(ulong *f)
30859cc4ca5SDavid du Colombier {
30959cc4ca5SDavid du Colombier 	int i, c;
31059cc4ca5SDavid du Colombier 
31159cc4ca5SDavid du Colombier 	c = 0;
31259cc4ca5SDavid du Colombier 	for(i=Prec-1; i>0; i--) {
31359cc4ca5SDavid du Colombier 		f[i] += c;
31459cc4ca5SDavid du Colombier 		c = f[i] >> Nbits;
31559cc4ca5SDavid du Colombier 		f[i] &= One-1;
31659cc4ca5SDavid du Colombier 	}
31759cc4ca5SDavid du Colombier 	f[0] += c;
31859cc4ca5SDavid du Colombier }
31959cc4ca5SDavid du Colombier 
32059cc4ca5SDavid du Colombier static int
fpcmp(char * a,ulong * f)32159cc4ca5SDavid du Colombier fpcmp(char *a, ulong* f)
32259cc4ca5SDavid du Colombier {
32359cc4ca5SDavid du Colombier 	ulong tf[Prec];
32459cc4ca5SDavid du Colombier 	int i, d, c;
32559cc4ca5SDavid du Colombier 
32659cc4ca5SDavid du Colombier 	for(i=0; i<Prec; i++)
32759cc4ca5SDavid du Colombier 		tf[i] = f[i];
32859cc4ca5SDavid du Colombier 
32959cc4ca5SDavid du Colombier 	for(;;) {
33059cc4ca5SDavid du Colombier 		/* tf *= 10 */
33159cc4ca5SDavid du Colombier 		for(i=0; i<Prec; i++)
33259cc4ca5SDavid du Colombier 			tf[i] = tf[i]*10;
33359cc4ca5SDavid du Colombier 		frnorm(tf);
33459cc4ca5SDavid du Colombier 		d = (tf[0] >> Nbits) + '0';
33559cc4ca5SDavid du Colombier 		tf[0] &= One-1;
33659cc4ca5SDavid du Colombier 
33759cc4ca5SDavid du Colombier 		/* compare next digit */
33859cc4ca5SDavid du Colombier 		c = *a;
33959cc4ca5SDavid du Colombier 		if(c == 0) {
34059cc4ca5SDavid du Colombier 			if('0' < d)
34159cc4ca5SDavid du Colombier 				return -1;
34259cc4ca5SDavid du Colombier 			if(tf[0] != 0)
34359cc4ca5SDavid du Colombier 				goto cont;
34459cc4ca5SDavid du Colombier 			for(i=1; i<Prec; i++)
34559cc4ca5SDavid du Colombier 				if(tf[i] != 0)
34659cc4ca5SDavid du Colombier 					goto cont;
34759cc4ca5SDavid du Colombier 			return 0;
34859cc4ca5SDavid du Colombier 		}
34959cc4ca5SDavid du Colombier 		if(c > d)
35059cc4ca5SDavid du Colombier 			return +1;
35159cc4ca5SDavid du Colombier 		if(c < d)
35259cc4ca5SDavid du Colombier 			return -1;
35359cc4ca5SDavid du Colombier 		a++;
35459cc4ca5SDavid du Colombier 	cont:;
35559cc4ca5SDavid du Colombier 	}
35659cc4ca5SDavid du Colombier }
35759cc4ca5SDavid du Colombier 
35859cc4ca5SDavid du Colombier static void
_divby(char * a,int * na,int b)359*9a747e4fSDavid du Colombier _divby(char *a, int *na, int b)
36059cc4ca5SDavid du Colombier {
36159cc4ca5SDavid du Colombier 	int n, c;
36259cc4ca5SDavid du Colombier 	char *p;
36359cc4ca5SDavid du Colombier 
36459cc4ca5SDavid du Colombier 	p = a;
36559cc4ca5SDavid du Colombier 	n = 0;
36659cc4ca5SDavid du Colombier 	while(n>>b == 0) {
36759cc4ca5SDavid du Colombier 		c = *a++;
36859cc4ca5SDavid du Colombier 		if(c == 0) {
36959cc4ca5SDavid du Colombier 			while(n) {
37059cc4ca5SDavid du Colombier 				c = n*10;
37159cc4ca5SDavid du Colombier 				if(c>>b)
37259cc4ca5SDavid du Colombier 					break;
37359cc4ca5SDavid du Colombier 				n = c;
37459cc4ca5SDavid du Colombier 			}
37559cc4ca5SDavid du Colombier 			goto xx;
37659cc4ca5SDavid du Colombier 		}
37759cc4ca5SDavid du Colombier 		n = n*10 + c-'0';
37859cc4ca5SDavid du Colombier 		(*na)--;
37959cc4ca5SDavid du Colombier 	}
38059cc4ca5SDavid du Colombier 	for(;;) {
38159cc4ca5SDavid du Colombier 		c = n>>b;
38259cc4ca5SDavid du Colombier 		n -= c<<b;
38359cc4ca5SDavid du Colombier 		*p++ = c + '0';
38459cc4ca5SDavid du Colombier 		c = *a++;
38559cc4ca5SDavid du Colombier 		if(c == 0)
38659cc4ca5SDavid du Colombier 			break;
38759cc4ca5SDavid du Colombier 		n = n*10 + c-'0';
38859cc4ca5SDavid du Colombier 	}
38959cc4ca5SDavid du Colombier 	(*na)++;
39059cc4ca5SDavid du Colombier xx:
39159cc4ca5SDavid du Colombier 	while(n) {
39259cc4ca5SDavid du Colombier 		n = n*10;
39359cc4ca5SDavid du Colombier 		c = n>>b;
39459cc4ca5SDavid du Colombier 		n -= c<<b;
39559cc4ca5SDavid du Colombier 		*p++ = c + '0';
39659cc4ca5SDavid du Colombier 		(*na)++;
39759cc4ca5SDavid du Colombier 	}
39859cc4ca5SDavid du Colombier 	*p = 0;
39959cc4ca5SDavid du Colombier }
40059cc4ca5SDavid du Colombier 
401*9a747e4fSDavid du Colombier static void
divby(char * a,int * na,int b)402*9a747e4fSDavid du Colombier divby(char *a, int *na, int b)
403*9a747e4fSDavid du Colombier {
404*9a747e4fSDavid du Colombier 	while(b > 9){
405*9a747e4fSDavid du Colombier 		_divby(a, na, 9);
406*9a747e4fSDavid du Colombier 		a[*na] = 0;
407*9a747e4fSDavid du Colombier 		b -= 9;
408*9a747e4fSDavid du Colombier 	}
409*9a747e4fSDavid du Colombier 	if(b > 0)
410*9a747e4fSDavid du Colombier 		_divby(a, na, b);
411*9a747e4fSDavid du Colombier }
412*9a747e4fSDavid du Colombier 
41359cc4ca5SDavid du Colombier static	Tab	tab1[] =
41459cc4ca5SDavid du Colombier {
41559cc4ca5SDavid du Colombier 	 1,  0, "",
41659cc4ca5SDavid du Colombier 	 3,  1, "7",
41759cc4ca5SDavid du Colombier 	 6,  2, "63",
41859cc4ca5SDavid du Colombier 	 9,  3, "511",
41959cc4ca5SDavid du Colombier 	13,  4, "8191",
42059cc4ca5SDavid du Colombier 	16,  5, "65535",
42159cc4ca5SDavid du Colombier 	19,  6, "524287",
42259cc4ca5SDavid du Colombier 	23,  7, "8388607",
42359cc4ca5SDavid du Colombier 	26,  8, "67108863",
42459cc4ca5SDavid du Colombier 	27,  9, "134217727",
42559cc4ca5SDavid du Colombier };
42659cc4ca5SDavid du Colombier 
42759cc4ca5SDavid du Colombier static void
divascii(char * a,int * na,int * dp,int * bp)42859cc4ca5SDavid du Colombier divascii(char *a, int *na, int *dp, int *bp)
42959cc4ca5SDavid du Colombier {
43059cc4ca5SDavid du Colombier 	int b, d;
43159cc4ca5SDavid du Colombier 	Tab *t;
43259cc4ca5SDavid du Colombier 
43359cc4ca5SDavid du Colombier 	d = *dp;
43459cc4ca5SDavid du Colombier 	if(d >= nelem(tab1))
43559cc4ca5SDavid du Colombier 		d = nelem(tab1)-1;
43659cc4ca5SDavid du Colombier 	t = tab1 + d;
43759cc4ca5SDavid du Colombier 	b = t->bp;
43859cc4ca5SDavid du Colombier 	if(memcmp(a, t->cmp, t->siz) > 0)
43959cc4ca5SDavid du Colombier 		d--;
44059cc4ca5SDavid du Colombier 	*dp -= d;
44159cc4ca5SDavid du Colombier 	*bp += b;
44259cc4ca5SDavid du Colombier 	divby(a, na, b);
44359cc4ca5SDavid du Colombier }
44459cc4ca5SDavid du Colombier 
44559cc4ca5SDavid du Colombier static void
mulby(char * a,char * p,char * q,int b)44659cc4ca5SDavid du Colombier mulby(char *a, char *p, char *q, int b)
44759cc4ca5SDavid du Colombier {
44859cc4ca5SDavid du Colombier 	int n, c;
44959cc4ca5SDavid du Colombier 
45059cc4ca5SDavid du Colombier 	n = 0;
45159cc4ca5SDavid du Colombier 	*p = 0;
45259cc4ca5SDavid du Colombier 	for(;;) {
45359cc4ca5SDavid du Colombier 		q--;
45459cc4ca5SDavid du Colombier 		if(q < a)
45559cc4ca5SDavid du Colombier 			break;
45659cc4ca5SDavid du Colombier 		c = *q - '0';
45759cc4ca5SDavid du Colombier 		c = (c<<b) + n;
45859cc4ca5SDavid du Colombier 		n = c/10;
45959cc4ca5SDavid du Colombier 		c -= n*10;
46059cc4ca5SDavid du Colombier 		p--;
46159cc4ca5SDavid du Colombier 		*p = c + '0';
46259cc4ca5SDavid du Colombier 	}
46359cc4ca5SDavid du Colombier 	while(n) {
46459cc4ca5SDavid du Colombier 		c = n;
46559cc4ca5SDavid du Colombier 		n = c/10;
46659cc4ca5SDavid du Colombier 		c -= n*10;
46759cc4ca5SDavid du Colombier 		p--;
46859cc4ca5SDavid du Colombier 		*p = c + '0';
46959cc4ca5SDavid du Colombier 	}
47059cc4ca5SDavid du Colombier }
47159cc4ca5SDavid du Colombier 
47259cc4ca5SDavid du Colombier static	Tab	tab2[] =
47359cc4ca5SDavid du Colombier {
47459cc4ca5SDavid du Colombier 	 1,  1, "",				// dp = 0-0
47559cc4ca5SDavid du Colombier 	 3,  3, "125",
47659cc4ca5SDavid du Colombier 	 6,  5, "15625",
47759cc4ca5SDavid du Colombier 	 9,  7, "1953125",
47859cc4ca5SDavid du Colombier 	13, 10, "1220703125",
47959cc4ca5SDavid du Colombier 	16, 12, "152587890625",
48059cc4ca5SDavid du Colombier 	19, 14, "19073486328125",
48159cc4ca5SDavid du Colombier 	23, 17, "11920928955078125",
48259cc4ca5SDavid du Colombier 	26, 19, "1490116119384765625",
48359cc4ca5SDavid du Colombier 	27, 19, "7450580596923828125",		// dp 8-9
48459cc4ca5SDavid du Colombier };
48559cc4ca5SDavid du Colombier 
48659cc4ca5SDavid du Colombier static void
mulascii(char * a,int * na,int * dp,int * bp)48759cc4ca5SDavid du Colombier mulascii(char *a, int *na, int *dp, int *bp)
48859cc4ca5SDavid du Colombier {
48959cc4ca5SDavid du Colombier 	char *p;
49059cc4ca5SDavid du Colombier 	int d, b;
49159cc4ca5SDavid du Colombier 	Tab *t;
49259cc4ca5SDavid du Colombier 
49359cc4ca5SDavid du Colombier 	d = -*dp;
49459cc4ca5SDavid du Colombier 	if(d >= nelem(tab2))
49559cc4ca5SDavid du Colombier 		d = nelem(tab2)-1;
49659cc4ca5SDavid du Colombier 	t = tab2 + d;
49759cc4ca5SDavid du Colombier 	b = t->bp;
49859cc4ca5SDavid du Colombier 	if(memcmp(a, t->cmp, t->siz) < 0)
49959cc4ca5SDavid du Colombier 		d--;
50059cc4ca5SDavid du Colombier 	p = a + *na;
50159cc4ca5SDavid du Colombier 	*bp -= b;
50259cc4ca5SDavid du Colombier 	*dp += d;
50359cc4ca5SDavid du Colombier 	*na += d;
50459cc4ca5SDavid du Colombier 	mulby(a, p+d, p, b);
50559cc4ca5SDavid du Colombier }
50659cc4ca5SDavid du Colombier 
50759cc4ca5SDavid du Colombier static int
xcmp(char * a,char * b)50859cc4ca5SDavid du Colombier xcmp(char *a, char *b)
50959cc4ca5SDavid du Colombier {
51059cc4ca5SDavid du Colombier 	int c1, c2;
51159cc4ca5SDavid du Colombier 
51259cc4ca5SDavid du Colombier 	while(c1 = *b++) {
51359cc4ca5SDavid du Colombier 		c2 = *a++;
51459cc4ca5SDavid du Colombier 		if(isupper(c2))
51559cc4ca5SDavid du Colombier 			c2 = tolower(c2);
51659cc4ca5SDavid du Colombier 		if(c1 != c2)
51759cc4ca5SDavid du Colombier 			return 1;
51859cc4ca5SDavid du Colombier 	}
51959cc4ca5SDavid du Colombier 	return 0;
52059cc4ca5SDavid du Colombier }
521