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