freebsd-dev/contrib/ntp/libntp/dolfptoa.c
Xin LI 3311ff84ea MFV r293415:
ntp 4.2.8p5

Reviewed by:	cy, roberto
Relnotes:	yes
Differential Revision:	https://reviews.freebsd.org/D4828
2016-01-08 15:53:48 +00:00

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 = (int)(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 = (int)(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 = (int)(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);
}