xref: /freebsd/contrib/ntp/libntp/dolfptoa.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1c0b746e5SOllivier Robert /*
2c0b746e5SOllivier Robert  * dolfptoa - do the grunge work of converting an l_fp number to decimal
3c0b746e5SOllivier Robert  */
42b15cb3dSCy Schubert #include <config.h>
5c0b746e5SOllivier Robert #include <stdio.h>
6c0b746e5SOllivier Robert 
7c0b746e5SOllivier Robert #include "ntp_fp.h"
8c0b746e5SOllivier Robert #include "ntp_stdlib.h"
9c0b746e5SOllivier Robert 
10c0b746e5SOllivier Robert char *
11c0b746e5SOllivier Robert dolfptoa(
122b15cb3dSCy Schubert 	u_int32 fpi,
132b15cb3dSCy Schubert 	u_int32 fpv,
14*2d4e511cSCy Schubert 	char sign,
159c2daa00SOllivier Robert 	short ndec,
16c0b746e5SOllivier Robert 	int msec
17c0b746e5SOllivier Robert 	)
18c0b746e5SOllivier Robert {
192b15cb3dSCy Schubert 	u_char *cp, *cpend, *cpdec;
202b15cb3dSCy Schubert 	int dec;
21c0b746e5SOllivier Robert 	u_char cbuf[24];
222b15cb3dSCy Schubert 	char *buf, *bp;
23c0b746e5SOllivier Robert 
24c0b746e5SOllivier Robert 	/*
25c0b746e5SOllivier Robert 	 * Get a string buffer before starting
26c0b746e5SOllivier Robert 	 */
27c0b746e5SOllivier Robert 	LIB_GETBUF(buf);
28c0b746e5SOllivier Robert 
29c0b746e5SOllivier Robert 	/*
30c0b746e5SOllivier Robert 	 * Zero the character buffer
31c0b746e5SOllivier Robert 	 */
322b15cb3dSCy Schubert 	ZERO(cbuf);
33c0b746e5SOllivier Robert 
34c0b746e5SOllivier Robert 	/*
352b15cb3dSCy Schubert 	 * Work on the integral part. This should work reasonable on
362b15cb3dSCy Schubert 	 * all machines with 32 bit arithmetic. Please note that 32 bits
372b15cb3dSCy Schubert 	 * can *always* be represented with at most 10 decimal digits,
382b15cb3dSCy Schubert 	 * including a possible rounding from the fractional part.
39ea906c41SOllivier Robert 	 */
402b15cb3dSCy Schubert 	cp = cpend = cpdec = &cbuf[10];
413311ff84SXin LI 	for (dec = (int)(cp - cbuf); dec > 0 && fpi != 0; dec--) {
422b15cb3dSCy Schubert 		/* can add another digit */
432b15cb3dSCy Schubert 		u_int32 digit;
44ea906c41SOllivier Robert 
452b15cb3dSCy Schubert 		digit  = fpi;
462b15cb3dSCy Schubert 		fpi   /= 10U;
472b15cb3dSCy Schubert 		digit -= (fpi << 3) + (fpi << 1); /* i*10 */
482b15cb3dSCy Schubert 		*--cp  = (u_char)digit;
49c0b746e5SOllivier Robert 	}
50c0b746e5SOllivier Robert 
51c0b746e5SOllivier Robert 	/*
52c0b746e5SOllivier Robert 	 * Done that, now deal with the problem of the fraction.  First
53c0b746e5SOllivier Robert 	 * determine the number of decimal places.
54c0b746e5SOllivier Robert 	 */
55c0b746e5SOllivier Robert 	dec = ndec;
56c0b746e5SOllivier Robert 	if (dec < 0)
57c0b746e5SOllivier Robert 		dec = 0;
582b15cb3dSCy Schubert 	if (msec) {
592b15cb3dSCy Schubert 		dec   += 3;
602b15cb3dSCy Schubert 		cpdec += 3;
61c0b746e5SOllivier Robert 	}
622b15cb3dSCy Schubert 	if ((size_t)dec > sizeof(cbuf) - (cpend - cbuf))
633311ff84SXin LI 		dec = (int)(sizeof(cbuf) - (cpend - cbuf));
64c0b746e5SOllivier Robert 
65c0b746e5SOllivier Robert 	/*
66c0b746e5SOllivier Robert 	 * If there's a fraction to deal with, do so.
67c0b746e5SOllivier Robert 	 */
682b15cb3dSCy Schubert 	for (/*NOP*/;  dec > 0 && fpv != 0;  dec--)  {
692b15cb3dSCy Schubert 		u_int32 digit, tmph, tmpl;
70c0b746e5SOllivier Robert 
71c0b746e5SOllivier Robert 		/*
722b15cb3dSCy Schubert 		 * The scheme here is to multiply the fraction
732b15cb3dSCy Schubert 		 * (0.1234...) by ten.  This moves a junk of BCD into
742b15cb3dSCy Schubert 		 * the units part.  record that and iterate.
752b15cb3dSCy Schubert 		 * multiply by shift/add in two dwords.
76c0b746e5SOllivier Robert 		 */
772b15cb3dSCy Schubert 		digit = 0;
782b15cb3dSCy Schubert 		M_LSHIFT(digit, fpv);
792b15cb3dSCy Schubert 		tmph = digit;
802b15cb3dSCy Schubert 		tmpl = fpv;
812b15cb3dSCy Schubert 		M_LSHIFT(digit, fpv);
822b15cb3dSCy Schubert 		M_LSHIFT(digit, fpv);
832b15cb3dSCy Schubert 		M_ADD(digit, fpv, tmph, tmpl);
842b15cb3dSCy Schubert 		*cpend++ = (u_char)digit;
85c0b746e5SOllivier Robert 	}
86c0b746e5SOllivier Robert 
872b15cb3dSCy Schubert 	/* decide whether to round or simply extend by zeros */
882b15cb3dSCy Schubert 	if (dec > 0) {
892b15cb3dSCy Schubert 		/* only '0' digits left -- just reposition end */
902b15cb3dSCy Schubert 		cpend += dec;
912b15cb3dSCy Schubert 	} else {
922b15cb3dSCy Schubert 		/* some bits remain in 'fpv'; do round */
932b15cb3dSCy Schubert 		u_char *tp    = cpend;
942b15cb3dSCy Schubert 		int     carry = ((fpv & 0x80000000) != 0);
95c0b746e5SOllivier Robert 
963311ff84SXin LI 		for (dec = (int)(tp - cbuf);  carry && dec > 0;  dec--) {
972b15cb3dSCy Schubert 			*--tp += 1;
982b15cb3dSCy Schubert 			if (*tp == 10)
99c0b746e5SOllivier Robert 				*tp = 0;
1002b15cb3dSCy Schubert 			else
1012b15cb3dSCy Schubert 				carry = FALSE;
1022b15cb3dSCy Schubert 		}
1032b15cb3dSCy Schubert 
1042b15cb3dSCy Schubert 		if (tp < cp) /* rounding from 999 to 1000 or similiar? */
105c0b746e5SOllivier Robert 			cp = tp;
106c0b746e5SOllivier Robert 	}
107c0b746e5SOllivier Robert 
108c0b746e5SOllivier Robert 	/*
109c0b746e5SOllivier Robert 	 * We've now got the fraction in cbuf[], with cp pointing at
110c0b746e5SOllivier Robert 	 * the first character, cpend pointing past the last, and
111c0b746e5SOllivier Robert 	 * cpdec pointing at the first character past the decimal.
112c0b746e5SOllivier Robert 	 * Remove leading zeros, then format the number into the
113c0b746e5SOllivier Robert 	 * buffer.
114c0b746e5SOllivier Robert 	 */
1152b15cb3dSCy Schubert 	while (cp < cpdec && *cp == 0)
116c0b746e5SOllivier Robert 		cp++;
1172b15cb3dSCy Schubert 	if (cp >= cpdec)
1182b15cb3dSCy Schubert 		cp = cpdec - 1;
119c0b746e5SOllivier Robert 
120c0b746e5SOllivier Robert 	bp = buf;
121*2d4e511cSCy Schubert 	if (sign)
122*2d4e511cSCy Schubert 		*bp++ = sign;
123c0b746e5SOllivier Robert 	while (cp < cpend) {
124c0b746e5SOllivier Robert 		if (cp == cpdec)
125c0b746e5SOllivier Robert 			*bp++ = '.';
1262b15cb3dSCy Schubert 		*bp++ = (char)(*cp++) + '0';
127c0b746e5SOllivier Robert 	}
128c0b746e5SOllivier Robert 	*bp = '\0';
129c0b746e5SOllivier Robert 
130c0b746e5SOllivier Robert 	/*
131c0b746e5SOllivier Robert 	 * Done!
132c0b746e5SOllivier Robert 	 */
133c0b746e5SOllivier Robert 	return buf;
134c0b746e5SOllivier Robert }
1352b15cb3dSCy Schubert 
1362b15cb3dSCy Schubert 
1372b15cb3dSCy Schubert char *
1382b15cb3dSCy Schubert mfptoa(
1392b15cb3dSCy Schubert 	u_int32	fpi,
1402b15cb3dSCy Schubert 	u_int32	fpf,
1412b15cb3dSCy Schubert 	short	ndec
1422b15cb3dSCy Schubert 	)
1432b15cb3dSCy Schubert {
1442b15cb3dSCy Schubert 	int	isneg;
1452b15cb3dSCy Schubert 
1462b15cb3dSCy Schubert 	isneg = M_ISNEG(fpi);
1472b15cb3dSCy Schubert 	if (isneg) {
1482b15cb3dSCy Schubert 		M_NEG(fpi, fpf);
1492b15cb3dSCy Schubert 	}
1502b15cb3dSCy Schubert 
151*2d4e511cSCy Schubert 	return dolfptoa(fpi, fpf, (isneg?'-':'+'), ndec, FALSE);
1522b15cb3dSCy Schubert }
1532b15cb3dSCy Schubert 
1542b15cb3dSCy Schubert 
1552b15cb3dSCy Schubert char *
1562b15cb3dSCy Schubert mfptoms(
1572b15cb3dSCy Schubert 	u_int32	fpi,
1582b15cb3dSCy Schubert 	u_int32	fpf,
1592b15cb3dSCy Schubert 	short	ndec
1602b15cb3dSCy Schubert 	)
1612b15cb3dSCy Schubert {
1622b15cb3dSCy Schubert 	int	isneg;
1632b15cb3dSCy Schubert 
1642b15cb3dSCy Schubert 	isneg = M_ISNEG(fpi);
1652b15cb3dSCy Schubert 	if (isneg) {
1662b15cb3dSCy Schubert 		M_NEG(fpi, fpf);
1672b15cb3dSCy Schubert 	}
1682b15cb3dSCy Schubert 
169*2d4e511cSCy Schubert 	return dolfptoa(fpi, fpf, (isneg?'-':'+'), ndec, TRUE);
1702b15cb3dSCy Schubert }
1712b15cb3dSCy Schubert 
1722b15cb3dSCy Schubert 
173