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