FreeCalypso > hg > ffs-editor
diff src/libsys/sprintf/float.c @ 0:92470e5d0b9e
src: partial import from FC Selenite
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Fri, 15 May 2020 01:28:16 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libsys/sprintf/float.c Fri May 15 01:28:16 2020 +0000 @@ -0,0 +1,122 @@ +/* + * Embedded [v]sprintf() implementation by Mychaela Falconia, + * loosely based on the 4.3BSD-Tahoe version. + * + * This module contains the floating point conversion functions. + */ + +#include <sys/types.h> +#include <ctype.h> +#include "defs.h" + +extern double modf(); + +extern u_char * _sprintf_field(u_char *op, int width, int flags, int sign, + u_char *body, int size, int dprec, int fpprec); + +#define MAX_INT_DIGITS 10 /* 2^32-1 in decimal */ +#define MAXFRACT 16 /* max sensible for 64-bit double */ +#define DEFPREC 6 + +static char * +emit_integer_portion(unsigned number, char *endp) +{ + char *t = endp; + + do { + *--t = tochar(number % 10); + number /= 10; + } while (number); + return (t); +} + +static int +f_round(double fract, char *start, char *end, int sign) +{ + double tmp; + + (void)modf(fract * 10, &tmp); + if (tmp > 4) + for (;; --end) { + if (*end == '.') + --end; + if (++*end <= '9') + break; + *end = '0'; + if (end == start) { + *--end = '1'; + return(1); + } + } + /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ + else if (sign == '-') + for (;; --end) { + if (*end == '.') + --end; + if (*end != '0') + break; + if (end == start) + return(-1); /* clear the -ve */ + } + return(0); +} + +u_char * +_sprintf_percent_f(u_char *op, int width, int flags, int sign, + double number, int prec) +{ + char buf[MAX_INT_DIGITS + 1 + MAXFRACT]; + int extra_prec = 0; + int origsign = sign; + int round_stat; + double fract, tmp; + char *start, *t; + + /* first order of business: weed out infinities and NaNs */ + if (isinf(number)) { + if (number < 0) + sign = '-'; + return _sprintf_field(op, width, flags, sign, "Inf", 3, 0, 0); + } + if (isnan(number)) + return _sprintf_field(op, width, flags, sign, "NaN", 3, 0, 0); + /* OK, we know it's a valid real like in the good old VAX days */ + if (number < 0) { + sign = '-'; + number = -number; + } + fract = modf(number, &tmp); + if (tmp > (double) 0xFFFFFFFF) + return _sprintf_field(op, width, flags, sign, "Toobig", 6, + 0, 0); + start = emit_integer_portion((unsigned) tmp, buf + MAX_INT_DIGITS); + if (prec > MAXFRACT) { + extra_prec = prec - MAXFRACT; + prec = MAXFRACT; + } else if (prec == -1) + prec = DEFPREC; + t = buf + MAX_INT_DIGITS; + /* + * if precision required or alternate flag set, add in a + * decimal point. + */ + if (prec || flags&ALT) + *t++ = '.'; + /* if requires more precision and some fraction left */ + if (fract) { + if (prec) + do { + fract = modf(fract * 10, &tmp); + *t++ = tochar((int)tmp); + } while (--prec && fract); + if (fract) { + round_stat = f_round(fract, start, t - 1, sign); + if (round_stat == 1) + start--; + else if (round_stat == -1) + sign = origsign; + } + } + return _sprintf_field(op, width, flags, sign, start, t - start, + 0, prec + extra_prec); +}