154359Sroberto/* 254359Sroberto * dolfptoa - do the grunge work of converting an l_fp number to decimal 354359Sroberto */ 4290000Sglebius#include <config.h> 554359Sroberto#include <stdio.h> 654359Sroberto 754359Sroberto#include "ntp_fp.h" 854359Sroberto#include "lib_strbuf.h" 954359Sroberto#include "ntp_string.h" 1054359Sroberto#include "ntp_stdlib.h" 1154359Sroberto 1254359Srobertochar * 1354359Srobertodolfptoa( 14290000Sglebius u_int32 fpi, 15290000Sglebius u_int32 fpv, 1654359Sroberto int neg, 17132451Sroberto short ndec, 1854359Sroberto int msec 1954359Sroberto ) 2054359Sroberto{ 21290000Sglebius u_char *cp, *cpend, *cpdec; 22290000Sglebius int dec; 2354359Sroberto u_char cbuf[24]; 24290000Sglebius char *buf, *bp; 2554359Sroberto 2654359Sroberto /* 2754359Sroberto * Get a string buffer before starting 2854359Sroberto */ 2954359Sroberto LIB_GETBUF(buf); 3054359Sroberto 3154359Sroberto /* 3254359Sroberto * Zero the character buffer 3354359Sroberto */ 34290000Sglebius ZERO(cbuf); 3554359Sroberto 3654359Sroberto /* 37290000Sglebius * Work on the integral part. This should work reasonable on 38290000Sglebius * all machines with 32 bit arithmetic. Please note that 32 bits 39290000Sglebius * can *always* be represented with at most 10 decimal digits, 40290000Sglebius * including a possible rounding from the fractional part. 4154359Sroberto */ 42290000Sglebius cp = cpend = cpdec = &cbuf[10]; 43293894Sglebius for (dec = (int)(cp - cbuf); dec > 0 && fpi != 0; dec--) { 44290000Sglebius /* can add another digit */ 45290000Sglebius u_int32 digit; 46290000Sglebius 47290000Sglebius digit = fpi; 48290000Sglebius fpi /= 10U; 49290000Sglebius digit -= (fpi << 3) + (fpi << 1); /* i*10 */ 50290000Sglebius *--cp = (u_char)digit; 5154359Sroberto } 5254359Sroberto 5354359Sroberto /* 5454359Sroberto * Done that, now deal with the problem of the fraction. First 5554359Sroberto * determine the number of decimal places. 5654359Sroberto */ 57290000Sglebius dec = ndec; 58290000Sglebius if (dec < 0) 59290000Sglebius dec = 0; 6054359Sroberto if (msec) { 61290000Sglebius dec += 3; 62290000Sglebius cpdec += 3; 6354359Sroberto } 64290000Sglebius if ((size_t)dec > sizeof(cbuf) - (cpend - cbuf)) 65293894Sglebius dec = (int)(sizeof(cbuf) - (cpend - cbuf)); 6654359Sroberto 6754359Sroberto /* 6854359Sroberto * If there's a fraction to deal with, do so. 6954359Sroberto */ 70290000Sglebius for (/*NOP*/; dec > 0 && fpv != 0; dec--) { 71290000Sglebius u_int32 digit, tmph, tmpl; 72290000Sglebius 7354359Sroberto /* 74290000Sglebius * The scheme here is to multiply the fraction 75290000Sglebius * (0.1234...) by ten. This moves a junk of BCD into 76290000Sglebius * the units part. record that and iterate. 77290000Sglebius * multiply by shift/add in two dwords. 7854359Sroberto */ 79290000Sglebius digit = 0; 80290000Sglebius M_LSHIFT(digit, fpv); 81290000Sglebius tmph = digit; 82290000Sglebius tmpl = fpv; 83290000Sglebius M_LSHIFT(digit, fpv); 84290000Sglebius M_LSHIFT(digit, fpv); 85290000Sglebius M_ADD(digit, fpv, tmph, tmpl); 86290000Sglebius *cpend++ = (u_char)digit; 87290000Sglebius } 8854359Sroberto 89290000Sglebius /* decide whether to round or simply extend by zeros */ 90290000Sglebius if (dec > 0) { 91290000Sglebius /* only '0' digits left -- just reposition end */ 92290000Sglebius cpend += dec; 93290000Sglebius } else { 94290000Sglebius /* some bits remain in 'fpv'; do round */ 95290000Sglebius u_char *tp = cpend; 96290000Sglebius int carry = ((fpv & 0x80000000) != 0); 97290000Sglebius 98293894Sglebius for (dec = (int)(tp - cbuf); carry && dec > 0; dec--) { 99290000Sglebius *--tp += 1; 100290000Sglebius if (*tp == 10) 10154359Sroberto *tp = 0; 102290000Sglebius else 103290000Sglebius carry = FALSE; 10454359Sroberto } 105290000Sglebius 106290000Sglebius if (tp < cp) /* rounding from 999 to 1000 or similiar? */ 107290000Sglebius cp = tp; 10854359Sroberto } 10954359Sroberto 11054359Sroberto /* 11154359Sroberto * We've now got the fraction in cbuf[], with cp pointing at 11254359Sroberto * the first character, cpend pointing past the last, and 11354359Sroberto * cpdec pointing at the first character past the decimal. 11454359Sroberto * Remove leading zeros, then format the number into the 11554359Sroberto * buffer. 11654359Sroberto */ 117290000Sglebius while (cp < cpdec && *cp == 0) 11854359Sroberto cp++; 119290000Sglebius if (cp >= cpdec) 120290000Sglebius cp = cpdec - 1; 12154359Sroberto 12254359Sroberto bp = buf; 12354359Sroberto if (neg) 124290000Sglebius *bp++ = '-'; 12554359Sroberto while (cp < cpend) { 12654359Sroberto if (cp == cpdec) 127290000Sglebius *bp++ = '.'; 128290000Sglebius *bp++ = (char)(*cp++) + '0'; 12954359Sroberto } 13054359Sroberto *bp = '\0'; 13154359Sroberto 13254359Sroberto /* 13354359Sroberto * Done! 13454359Sroberto */ 13554359Sroberto return buf; 13654359Sroberto} 137290000Sglebius 138290000Sglebius 139290000Sglebiuschar * 140290000Sglebiusmfptoa( 141290000Sglebius u_int32 fpi, 142290000Sglebius u_int32 fpf, 143290000Sglebius short ndec 144290000Sglebius ) 145290000Sglebius{ 146290000Sglebius int isneg; 147290000Sglebius 148290000Sglebius isneg = M_ISNEG(fpi); 149290000Sglebius if (isneg) { 150290000Sglebius M_NEG(fpi, fpf); 151290000Sglebius } 152290000Sglebius 153290000Sglebius return dolfptoa(fpi, fpf, isneg, ndec, FALSE); 154290000Sglebius} 155290000Sglebius 156290000Sglebius 157290000Sglebiuschar * 158290000Sglebiusmfptoms( 159290000Sglebius u_int32 fpi, 160290000Sglebius u_int32 fpf, 161290000Sglebius short ndec 162290000Sglebius ) 163290000Sglebius{ 164290000Sglebius int isneg; 165290000Sglebius 166290000Sglebius isneg = M_ISNEG(fpi); 167290000Sglebius if (isneg) { 168290000Sglebius M_NEG(fpi, fpf); 169290000Sglebius } 170290000Sglebius 171290000Sglebius return dolfptoa(fpi, fpf, isneg, ndec, TRUE); 172290000Sglebius} 173290000Sglebius 174290000Sglebius 175