NetBSD/dist/ntp/libntp/dolfptoa.c

176 lines
3.4 KiB
C

/* $NetBSD: dolfptoa.c,v 1.3 2006/06/11 19:34:10 kardel Exp $ */
/*
* dolfptoa - do the grunge work of converting an l_fp number to decimal
*/
#include <stdio.h>
#include "ntp_fp.h"
#include "lib_strbuf.h"
#include "ntp_string.h"
#include "ntp_stdlib.h"
char *
dolfptoa(
u_long fpi,
u_long fpv,
int neg,
short ndec,
int msec
)
{
register u_char *cp, *cpend;
register u_long lwork;
register int dec;
u_char cbuf[24];
u_char *cpdec;
char *buf;
char *bp;
/*
* Get a string buffer before starting
*/
LIB_GETBUF(buf);
/*
* Zero the character buffer
*/
memset((char *) cbuf, 0, sizeof(cbuf));
/*
* safeguard against sign extensions and other mishaps on 64 bit platforms
* the code following is designed for and only for 32-bit inputs and
* only 32-bit worth of input are supplied.
*/
fpi &= 0xffffffff;
fpv &= 0xffffffff;
/*
* Work on the integral part. This is biased by what I know
* compiles fairly well for a 68000.
*/
cp = cpend = &cbuf[10];
lwork = fpi;
if (lwork & 0xffff0000) {
register u_long lten = 10;
register u_long ltmp;
do {
ltmp = lwork;
lwork /= lten;
ltmp -= (lwork << 3) + (lwork << 1);
if (cp < cbuf) abort(); /* rather die a horrible death than trash the memory */
*--cp = (u_char)ltmp;
} while (lwork & 0xffff0000);
}
if (lwork != 0) {
register u_short sten = 10;
register u_short stmp;
register u_short swork = (u_short)lwork;
do {
stmp = swork;
swork = (u_short) (swork/sten);
stmp = (u_short)(stmp - ((swork<<3) + (swork<<1)));
if (cp < cbuf) abort(); /* rather die a horrible death than trash the memory */
*--cp = (u_char)stmp;
} while (swork != 0);
}
/*
* Done that, now deal with the problem of the fraction. First
* determine the number of decimal places.
*/
if (msec) {
dec = ndec + 3;
if (dec < 3)
dec = 3;
cpdec = &cbuf[13];
} else {
dec = ndec;
if (dec < 0)
dec = 0;
cpdec = &cbuf[10];
}
if (dec > 12)
dec = 12;
/*
* If there's a fraction to deal with, do so.
*/
if (fpv != 0) {
l_fp work;
work.l_ui = 0;
work.l_uf = fpv;
while (dec > 0) {
l_fp ftmp;
dec--;
/*
* 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.
*/
work.l_ui = 0;
L_LSHIFT(&work);
ftmp = work;
L_LSHIFT(&work);
L_LSHIFT(&work);
L_ADD(&work, &ftmp);
*cpend++ = (u_char)work.l_ui;
if (work.l_uf == 0)
break;
if (cpend > (cbuf + sizeof(cbuf))) abort(); /* rather die a horrible death than trash the memory */
}
/*
* Rounding is rotten
*/
if (work.l_uf & 0x80000000) {
register u_char *tp = cpend;
*(--tp) += 1;
while (*tp >= 10) {
*tp = 0;
*(--tp) += 1;
};
if (tp < cp)
cp = tp;
}
}
cpend += dec;
/*
* 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) {
if (*cp != 0)
break;
cp++;
}
if (cp == cpdec)
--cp;
bp = buf;
if (neg)
*bp++ = '-';
while (cp < cpend) {
if (cp == cpdec)
*bp++ = '.';
*bp++ = (char)(*cp++ + '0'); /* ascii dependent? */
}
*bp = '\0';
/*
* Done!
*/
return buf;
}