Home | History | Annotate | Line # | Download | only in libntp
      1 /*	$NetBSD: dolfptoa.c,v 1.6 2024/08/18 20:47:13 christos Exp $	*/
      2 
      3 /*
      4  * dolfptoa - do the grunge work of converting an l_fp number to decimal
      5  */
      6 #include <config.h>
      7 #include <stdio.h>
      8 
      9 #include "ntp_fp.h"
     10 #include "ntp_stdlib.h"
     11 
     12 char *
     13 dolfptoa(
     14 	u_int32 fpi,
     15 	u_int32 fpv,
     16 	char sign,
     17 	short ndec,
     18 	int msec
     19 	)
     20 {
     21 	u_char *cp, *cpend, *cpdec;
     22 	int dec;
     23 	u_char cbuf[24];
     24 	char *buf, *bp;
     25 
     26 	/*
     27 	 * Get a string buffer before starting
     28 	 */
     29 	LIB_GETBUF(buf);
     30 
     31 	/*
     32 	 * Zero the character buffer
     33 	 */
     34 	ZERO(cbuf);
     35 
     36 	/*
     37 	 * Work on the integral part. This should work reasonable on
     38 	 * all machines with 32 bit arithmetic. Please note that 32 bits
     39 	 * can *always* be represented with at most 10 decimal digits,
     40 	 * including a possible rounding from the fractional part.
     41 	 */
     42 	cp = cpend = cpdec = &cbuf[10];
     43 	for (dec = (int)(cp - cbuf); dec > 0 && fpi != 0; dec--) {
     44 		/* can add another digit */
     45 		u_int32 digit;
     46 
     47 		digit  = fpi;
     48 		fpi   /= 10U;
     49 		digit -= (fpi << 3) + (fpi << 1); /* i*10 */
     50 		*--cp  = (u_char)digit;
     51 	}
     52 
     53 	/*
     54 	 * Done that, now deal with the problem of the fraction.  First
     55 	 * determine the number of decimal places.
     56 	 */
     57 	dec = ndec;
     58 	if (dec < 0)
     59 		dec = 0;
     60 	if (msec) {
     61 		dec   += 3;
     62 		cpdec += 3;
     63 	}
     64 	if ((size_t)dec > sizeof(cbuf) - (cpend - cbuf))
     65 		dec = (int)(sizeof(cbuf) - (cpend - cbuf));
     66 
     67 	/*
     68 	 * If there's a fraction to deal with, do so.
     69 	 */
     70 	for (/*NOP*/;  dec > 0 && fpv != 0;  dec--)  {
     71 		u_int32 digit, tmph, tmpl;
     72 
     73 		/*
     74 		 * The scheme here is to multiply the fraction
     75 		 * (0.1234...) by ten.  This moves a junk of BCD into
     76 		 * the units part.  record that and iterate.
     77 		 * multiply by shift/add in two dwords.
     78 		 */
     79 		digit = 0;
     80 		M_LSHIFT(digit, fpv);
     81 		tmph = digit;
     82 		tmpl = fpv;
     83 		M_LSHIFT(digit, fpv);
     84 		M_LSHIFT(digit, fpv);
     85 		M_ADD(digit, fpv, tmph, tmpl);
     86 		*cpend++ = (u_char)digit;
     87 	}
     88 
     89 	/* decide whether to round or simply extend by zeros */
     90 	if (dec > 0) {
     91 		/* only '0' digits left -- just reposition end */
     92 		cpend += dec;
     93 	} else {
     94 		/* some bits remain in 'fpv'; do round */
     95 		u_char *tp    = cpend;
     96 		int     carry = ((fpv & 0x80000000) != 0);
     97 
     98 		for (dec = (int)(tp - cbuf);  carry && dec > 0;  dec--) {
     99 			*--tp += 1;
    100 			if (*tp == 10)
    101 				*tp = 0;
    102 			else
    103 				carry = FALSE;
    104 		}
    105 
    106 		if (tp < cp) /* rounding from 999 to 1000 or similiar? */
    107 			cp = tp;
    108 	}
    109 
    110 	/*
    111 	 * We've now got the fraction in cbuf[], with cp pointing at
    112 	 * the first character, cpend pointing past the last, and
    113 	 * cpdec pointing at the first character past the decimal.
    114 	 * Remove leading zeros, then format the number into the
    115 	 * buffer.
    116 	 */
    117 	while (cp < cpdec && *cp == 0)
    118 		cp++;
    119 	if (cp >= cpdec)
    120 		cp = cpdec - 1;
    121 
    122 	bp = buf;
    123 	if (sign)
    124 		*bp++ = sign;
    125 	while (cp < cpend) {
    126 		if (cp == cpdec)
    127 			*bp++ = '.';
    128 		*bp++ = (char)(*cp++) + '0';
    129 	}
    130 	*bp = '\0';
    131 
    132 	/*
    133 	 * Done!
    134 	 */
    135 	return buf;
    136 }
    137 
    138 
    139 char *
    140 mfptoa(
    141 	u_int32	fpi,
    142 	u_int32	fpf,
    143 	short	ndec
    144 	)
    145 {
    146 	int	isneg;
    147 
    148 	isneg = M_ISNEG(fpi);
    149 	if (isneg) {
    150 		M_NEG(fpi, fpf);
    151 	}
    152 
    153 	return dolfptoa(fpi, fpf, (isneg?'-':'+'), ndec, FALSE);
    154 }
    155 
    156 
    157 char *
    158 mfptoms(
    159 	u_int32	fpi,
    160 	u_int32	fpf,
    161 	short	ndec
    162 	)
    163 {
    164 	int	isneg;
    165 
    166 	isneg = M_ISNEG(fpi);
    167 	if (isneg) {
    168 		M_NEG(fpi, fpf);
    169 	}
    170 
    171 	return dolfptoa(fpi, fpf, (isneg?'-':'+'), ndec, TRUE);
    172 }
    173 
    174 
    175