2b15cb3d09
Thanks to roberto for providing pointers to wedge this into HEAD. Approved by: roberto
175 lines
3.1 KiB
C
175 lines
3.1 KiB
C
/*
|
|
* dolfptoa - do the grunge work of converting an l_fp number to decimal
|
|
*/
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
|
|
#include "ntp_fp.h"
|
|
#include "lib_strbuf.h"
|
|
#include "ntp_string.h"
|
|
#include "ntp_stdlib.h"
|
|
|
|
char *
|
|
dolfptoa(
|
|
u_int32 fpi,
|
|
u_int32 fpv,
|
|
int neg,
|
|
short ndec,
|
|
int msec
|
|
)
|
|
{
|
|
u_char *cp, *cpend, *cpdec;
|
|
int dec;
|
|
u_char cbuf[24];
|
|
char *buf, *bp;
|
|
|
|
/*
|
|
* Get a string buffer before starting
|
|
*/
|
|
LIB_GETBUF(buf);
|
|
|
|
/*
|
|
* Zero the character buffer
|
|
*/
|
|
ZERO(cbuf);
|
|
|
|
/*
|
|
* Work on the integral part. This should work reasonable on
|
|
* all machines with 32 bit arithmetic. Please note that 32 bits
|
|
* can *always* be represented with at most 10 decimal digits,
|
|
* including a possible rounding from the fractional part.
|
|
*/
|
|
cp = cpend = cpdec = &cbuf[10];
|
|
for (dec = cp - cbuf; dec > 0 && fpi != 0; dec--) {
|
|
/* can add another digit */
|
|
u_int32 digit;
|
|
|
|
digit = fpi;
|
|
fpi /= 10U;
|
|
digit -= (fpi << 3) + (fpi << 1); /* i*10 */
|
|
*--cp = (u_char)digit;
|
|
}
|
|
|
|
/*
|
|
* Done that, now deal with the problem of the fraction. First
|
|
* determine the number of decimal places.
|
|
*/
|
|
dec = ndec;
|
|
if (dec < 0)
|
|
dec = 0;
|
|
if (msec) {
|
|
dec += 3;
|
|
cpdec += 3;
|
|
}
|
|
if ((size_t)dec > sizeof(cbuf) - (cpend - cbuf))
|
|
dec = sizeof(cbuf) - (cpend - cbuf);
|
|
|
|
/*
|
|
* If there's a fraction to deal with, do so.
|
|
*/
|
|
for (/*NOP*/; dec > 0 && fpv != 0; dec--) {
|
|
u_int32 digit, tmph, tmpl;
|
|
|
|
/*
|
|
* The scheme here is to multiply the fraction
|
|
* (0.1234...) by ten. This moves a junk of BCD into
|
|
* the units part. record that and iterate.
|
|
* multiply by shift/add in two dwords.
|
|
*/
|
|
digit = 0;
|
|
M_LSHIFT(digit, fpv);
|
|
tmph = digit;
|
|
tmpl = fpv;
|
|
M_LSHIFT(digit, fpv);
|
|
M_LSHIFT(digit, fpv);
|
|
M_ADD(digit, fpv, tmph, tmpl);
|
|
*cpend++ = (u_char)digit;
|
|
}
|
|
|
|
/* decide whether to round or simply extend by zeros */
|
|
if (dec > 0) {
|
|
/* only '0' digits left -- just reposition end */
|
|
cpend += dec;
|
|
} else {
|
|
/* some bits remain in 'fpv'; do round */
|
|
u_char *tp = cpend;
|
|
int carry = ((fpv & 0x80000000) != 0);
|
|
|
|
for (dec = tp - cbuf; carry && dec > 0; dec--) {
|
|
*--tp += 1;
|
|
if (*tp == 10)
|
|
*tp = 0;
|
|
else
|
|
carry = FALSE;
|
|
}
|
|
|
|
if (tp < cp) /* rounding from 999 to 1000 or similiar? */
|
|
cp = tp;
|
|
}
|
|
|
|
/*
|
|
* We've now got the fraction in cbuf[], with cp pointing at
|
|
* the first character, cpend pointing past the last, and
|
|
* cpdec pointing at the first character past the decimal.
|
|
* Remove leading zeros, then format the number into the
|
|
* buffer.
|
|
*/
|
|
while (cp < cpdec && *cp == 0)
|
|
cp++;
|
|
if (cp >= cpdec)
|
|
cp = cpdec - 1;
|
|
|
|
bp = buf;
|
|
if (neg)
|
|
*bp++ = '-';
|
|
while (cp < cpend) {
|
|
if (cp == cpdec)
|
|
*bp++ = '.';
|
|
*bp++ = (char)(*cp++) + '0';
|
|
}
|
|
*bp = '\0';
|
|
|
|
/*
|
|
* Done!
|
|
*/
|
|
return buf;
|
|
}
|
|
|
|
|
|
char *
|
|
mfptoa(
|
|
u_int32 fpi,
|
|
u_int32 fpf,
|
|
short ndec
|
|
)
|
|
{
|
|
int isneg;
|
|
|
|
isneg = M_ISNEG(fpi);
|
|
if (isneg) {
|
|
M_NEG(fpi, fpf);
|
|
}
|
|
|
|
return dolfptoa(fpi, fpf, isneg, ndec, FALSE);
|
|
}
|
|
|
|
|
|
char *
|
|
mfptoms(
|
|
u_int32 fpi,
|
|
u_int32 fpf,
|
|
short ndec
|
|
)
|
|
{
|
|
int isneg;
|
|
|
|
isneg = M_ISNEG(fpi);
|
|
if (isneg) {
|
|
M_NEG(fpi, fpf);
|
|
}
|
|
|
|
return dolfptoa(fpi, fpf, isneg, ndec, TRUE);
|
|
}
|
|
|
|
|