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