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 }