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