FreeCalypso > hg > fc-selenite
comparison src/libsys/sprintf/float.c @ 86:425ab6d987f3
src/libsys: pieced together from Citrine
| author | Mychaela Falconia <falcon@freecalypso.org> |
|---|---|
| date | Fri, 20 Jul 2018 20:36:19 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 85:4ef6808ea50e | 86:425ab6d987f3 |
|---|---|
| 1 /* | |
| 2 * Embedded [v]sprintf() implementation by Mychaela Falconia, | |
| 3 * loosely based on the 4.3BSD-Tahoe version. | |
| 4 * | |
| 5 * This module contains the floating point conversion functions. | |
| 6 */ | |
| 7 | |
| 8 #include <sys/types.h> | |
| 9 #include <ctype.h> | |
| 10 #include "defs.h" | |
| 11 | |
| 12 extern double modf(); | |
| 13 | |
| 14 extern u_char * _sprintf_field(u_char *op, int width, int flags, int sign, | |
| 15 u_char *body, int size, int dprec, int fpprec); | |
| 16 | |
| 17 #define MAX_INT_DIGITS 10 /* 2^32-1 in decimal */ | |
| 18 #define MAXFRACT 16 /* max sensible for 64-bit double */ | |
| 19 #define DEFPREC 6 | |
| 20 | |
| 21 static char * | |
| 22 emit_integer_portion(unsigned number, char *endp) | |
| 23 { | |
| 24 char *t = endp; | |
| 25 | |
| 26 do { | |
| 27 *--t = tochar(number % 10); | |
| 28 number /= 10; | |
| 29 } while (number); | |
| 30 return (t); | |
| 31 } | |
| 32 | |
| 33 static int | |
| 34 f_round(double fract, char *start, char *end, int sign) | |
| 35 { | |
| 36 double tmp; | |
| 37 | |
| 38 (void)modf(fract * 10, &tmp); | |
| 39 if (tmp > 4) | |
| 40 for (;; --end) { | |
| 41 if (*end == '.') | |
| 42 --end; | |
| 43 if (++*end <= '9') | |
| 44 break; | |
| 45 *end = '0'; | |
| 46 if (end == start) { | |
| 47 *--end = '1'; | |
| 48 return(1); | |
| 49 } | |
| 50 } | |
| 51 /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ | |
| 52 else if (sign == '-') | |
| 53 for (;; --end) { | |
| 54 if (*end == '.') | |
| 55 --end; | |
| 56 if (*end != '0') | |
| 57 break; | |
| 58 if (end == start) | |
| 59 return(-1); /* clear the -ve */ | |
| 60 } | |
| 61 return(0); | |
| 62 } | |
| 63 | |
| 64 u_char * | |
| 65 _sprintf_percent_f(u_char *op, int width, int flags, int sign, | |
| 66 double number, int prec) | |
| 67 { | |
| 68 char buf[MAX_INT_DIGITS + 1 + MAXFRACT]; | |
| 69 int extra_prec = 0; | |
| 70 int origsign = sign; | |
| 71 int round_stat; | |
| 72 double fract, tmp; | |
| 73 char *start, *t; | |
| 74 | |
| 75 /* first order of business: weed out infinities and NaNs */ | |
| 76 if (isinf(number)) { | |
| 77 if (number < 0) | |
| 78 sign = '-'; | |
| 79 return _sprintf_field(op, width, flags, sign, "Inf", 3, 0, 0); | |
| 80 } | |
| 81 if (isnan(number)) | |
| 82 return _sprintf_field(op, width, flags, sign, "NaN", 3, 0, 0); | |
| 83 /* OK, we know it's a valid real like in the good old VAX days */ | |
| 84 if (number < 0) { | |
| 85 sign = '-'; | |
| 86 number = -number; | |
| 87 } | |
| 88 fract = modf(number, &tmp); | |
| 89 if (tmp > (double) 0xFFFFFFFF) | |
| 90 return _sprintf_field(op, width, flags, sign, "Toobig", 6, | |
| 91 0, 0); | |
| 92 start = emit_integer_portion((unsigned) tmp, buf + MAX_INT_DIGITS); | |
| 93 if (prec > MAXFRACT) { | |
| 94 extra_prec = prec - MAXFRACT; | |
| 95 prec = MAXFRACT; | |
| 96 } else if (prec == -1) | |
| 97 prec = DEFPREC; | |
| 98 t = buf + MAX_INT_DIGITS; | |
| 99 /* | |
| 100 * if precision required or alternate flag set, add in a | |
| 101 * decimal point. | |
| 102 */ | |
| 103 if (prec || flags&ALT) | |
| 104 *t++ = '.'; | |
| 105 /* if requires more precision and some fraction left */ | |
| 106 if (fract) { | |
| 107 if (prec) | |
| 108 do { | |
| 109 fract = modf(fract * 10, &tmp); | |
| 110 *t++ = tochar((int)tmp); | |
| 111 } while (--prec && fract); | |
| 112 if (fract) { | |
| 113 round_stat = f_round(fract, start, t - 1, sign); | |
| 114 if (round_stat == 1) | |
| 115 start--; | |
| 116 else if (round_stat == -1) | |
| 117 sign = origsign; | |
| 118 } | |
| 119 } | |
| 120 return _sprintf_field(op, width, flags, sign, start, t - start, | |
| 121 0, prec + extra_prec); | |
| 122 } |
