1 #include "xmlrpc_config.h"
2
3 #include <assert.h>
4 #include <stdlib.h>
5 #include <float.h>
6 #include <math.h>
7
8 #include "xmlrpc-c/util.h"
9 #include "xmlrpc-c/util_int.h"
10
11 #include "double.h"
12
13 typedef struct {
14 char * bytes;
15 /* NULL means there has been a memory allocation failure.
16 bufferConcat() still works in this case, because we dont' want
17 callers to have to deal with the out-of-memory possibility;
18 it's just a no-op.
19 */
20 char * next;
21 char * end;
22 } buffer;
23
24
25 static void
bufferInit(buffer * const bufferP)26 bufferInit(buffer * const bufferP) {
27
28 unsigned int const initialSize = 64;
29
30 bufferP->bytes = malloc(initialSize);
31
32 if (bufferP->bytes) {
33 bufferP->next = bufferP->bytes;
34 bufferP->end = bufferP->bytes + initialSize;
35 }
36 }
37
38
39
40 static void
bufferConcat(buffer * const bufferP,char const newChar)41 bufferConcat(buffer * const bufferP,
42 char const newChar) {
43
44 if (bufferP->bytes) {
45 if (bufferP->next >= bufferP->end) {
46 size_t const oldSize = bufferP->end - bufferP->bytes;
47 size_t const newSize = oldSize + 64;
48 bufferP->bytes = realloc(bufferP->bytes, newSize);
49 bufferP->next = bufferP->bytes + oldSize;
50 bufferP->end = bufferP->bytes + newSize;
51 }
52
53 if (bufferP->bytes)
54 *(bufferP->next++) = newChar;
55 }
56 }
57
58
59
60 static char
digitChar(unsigned int const digitValue)61 digitChar(unsigned int const digitValue) {
62
63 assert(digitValue < 10);
64
65 return '0' + digitValue;
66 }
67
68
69
70 static unsigned int
leadDigit(double const arg,double const precision)71 leadDigit(double const arg,
72 double const precision) {
73 /*----------------------------------------------------------------------------
74 Assuming 'arg' has one digit before the decimal point (which may be zero),
75 return that digit.
76
77 We assume the precision of 'arg' is plus or minus 'precision', and bias our
78 estimation of the first digit up. We do that bias in order to bias toward
79 shorter decimal ciphers: It's cleaner to consider 2.9999999 to be 3 than to
80 consider 3 to be 2.999999.
81 -----------------------------------------------------------------------------*/
82 return MIN(9, (unsigned int)(arg + precision));
83 }
84
85
86
87 static void
floatWhole(double const value,buffer * const formattedP,double * const formattedAmountP,double * const precisionP)88 floatWhole(double const value,
89 buffer * const formattedP,
90 double * const formattedAmountP,
91 double * const precisionP) {
92 /*----------------------------------------------------------------------------
93 Format into *formattedP the whole part of 'value', i.e. the part before the
94 decimal point.
95
96 'value' is a finite number.
97
98 Return as *formattedAmountP the whole amount; e.g. if 'value' is 35.2,
99 we return *formattedAmountP = 35.
100
101 As there is imprecision involved in our calculations, return as *precisionP
102 the maximum difference there may be be between 'double' and what we
103 formatted.
104 -----------------------------------------------------------------------------*/
105 if (value < 1.0) {
106 /* No digits to add to the whole part */
107 *formattedAmountP = 0;
108 *precisionP = DBL_EPSILON;
109 } else {
110 double nonLeastAmount;
111 double nonLeastPrecision;
112 unsigned int leastValue;
113
114 /* Add all digits but the least significant to *formattedP */
115
116 floatWhole(value/10.0, formattedP, &nonLeastAmount,
117 &nonLeastPrecision);
118
119 /* Add the least significant digit to *formattedP */
120
121 if (nonLeastPrecision > 0.1) {
122 /* We're down in the noise now; no point in showing any more
123 significant digits (and we couldn't if we wanted to, because
124 nonLeastPrecision * 10 might be more than 10 less than
125 'value').
126 */
127 leastValue = 0;
128 } else
129 leastValue = leadDigit(value - nonLeastAmount * 10,
130 nonLeastPrecision * 10);
131
132 bufferConcat(formattedP, digitChar(leastValue));
133
134 *formattedAmountP = nonLeastAmount * 10 + leastValue;
135 *precisionP = nonLeastPrecision * 10;
136 }
137 }
138
139
140
141 static void
floatFractionPart(double const value,double const wholePrecision,buffer * const formattedP)142 floatFractionPart(double const value,
143 double const wholePrecision,
144 buffer * const formattedP) {
145 /*----------------------------------------------------------------------------
146 Serialize the part that comes after the decimal point, assuming there
147 is something (nonzero) before the decimal point that uses up all but
148 'wholePrecision' of the available precision.
149 -----------------------------------------------------------------------------*/
150 double precision;
151 double d;
152
153 assert(value < 1.0);
154
155 for (d = value, precision = wholePrecision;
156 d > precision;
157 precision *= 10) {
158
159 unsigned int digitValue;
160
161 d *= 10;
162 digitValue = leadDigit(d, precision);
163
164 d -= digitValue;
165
166 assert(d < 1.0);
167
168 bufferConcat(formattedP, digitChar(digitValue));
169 }
170 }
171
172
173
174 static void
floatFraction(double const value,buffer * const formattedP)175 floatFraction(double const value,
176 buffer * const formattedP) {
177 /*----------------------------------------------------------------------------
178 Serialize the part that comes after the decimal point, assuming there
179 is nothing before the decimal point.
180 -----------------------------------------------------------------------------*/
181 double precision;
182 double d;
183
184 assert(0.0 < value && value < 1.0);
185
186 /* Do the leading zeroes, which eat no precision */
187
188 for (d = value * 10; d < 1.0; d *= 10)
189 bufferConcat(formattedP, '0');
190
191 /* Now the significant digits */
192
193 precision = DBL_EPSILON;
194
195 while (d > precision) {
196 unsigned int const digitValue = leadDigit(d, precision);
197
198 bufferConcat(formattedP, digitChar(digitValue));
199
200 d -= digitValue;
201
202 assert(d < 1.0);
203
204 d *= 10;
205 precision *= 10;
206 }
207 }
208
209
210
211 static void
floatUnsigned(double const value,buffer * const formattedP)212 floatUnsigned(double const value,
213 buffer * const formattedP) {
214 /*----------------------------------------------------------------------------
215 Serialize 'value', assuming it is positive, and append it to *formattedP,
216 without a sign.
217 -----------------------------------------------------------------------------*/
218 assert(value >= 0.0);
219
220 if (value >= 1.0) {
221 double wholePart;
222 double wholePrecision;
223
224 floatWhole(value, formattedP, &wholePart, &wholePrecision);
225
226 if (wholePrecision >= 1.0) {
227 /* We ran out of precision before we got to the decimal
228 point
229 */
230 } else {
231 double const fractionPart = value - wholePart;
232
233 if (fractionPart > wholePrecision) {
234 bufferConcat(formattedP, '.');
235
236 floatFractionPart(fractionPart, wholePrecision,
237 formattedP);
238 }
239 }
240 } else {
241 bufferConcat(formattedP, '0');
242
243 if (value > 0.0) {
244 bufferConcat(formattedP, '.');
245 floatFraction(value, formattedP);
246 }
247 }
248 }
249
250
251
252 void
xmlrpc_formatFloat(xmlrpc_env * const envP,double const value,const char ** const formattedP)253 xmlrpc_formatFloat(xmlrpc_env * const envP,
254 double const value,
255 const char ** const formattedP) {
256 /*----------------------------------------------------------------------------
257 Format the value 'value' in XML-RPC - just the characters that represent
258 the numbers - none of the XML markup. E.g. "1.234".
259
260 Assume 'value' is finite, as there is no such thing as an infinite or
261 NaN value in XML-RPC.
262 -----------------------------------------------------------------------------*/
263 double absvalue;
264 buffer formatted;
265
266 assert(XMLRPC_FINITE(value));
267
268 bufferInit(&formatted);
269
270 if (value < 0.0) {
271 bufferConcat(&formatted, '-');
272 absvalue = - value;
273 } else
274 absvalue = value;
275
276 floatUnsigned(absvalue, &formatted);
277
278 bufferConcat(&formatted, '\0');
279
280 if (formatted.bytes == NULL)
281 xmlrpc_faultf(envP, "Couldn't allocate memory to format %g",
282 value);
283 else
284 *formattedP = formatted.bytes;
285 }
286