|
|
#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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return MIN(9, (unsigned int)(arg + precision)); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
floatWhole(double const value, |
|
|
buffer * const formattedP, |
|
|
double * const formattedAmountP, |
|
|
double * const precisionP) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (value < 1.0) { |
|
|
|
|
|
*formattedAmountP = 0; |
|
|
*precisionP = DBL_EPSILON; |
|
|
} else { |
|
|
double nonLeastAmount; |
|
|
double nonLeastPrecision; |
|
|
unsigned int leastValue; |
|
|
|
|
|
|
|
|
|
|
|
floatWhole(value/10.0, formattedP, &nonLeastAmount, |
|
|
&nonLeastPrecision); |
|
|
|
|
|
|
|
|
|
|
|
if (nonLeastPrecision > 0.1) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double precision; |
|
|
double d; |
|
|
|
|
|
assert(0.0 < value && value < 1.0); |
|
|
|
|
|
|
|
|
|
|
|
for (d = value * 10; d < 1.0; d *= 10) |
|
|
bufferConcat(formattedP, '0'); |
|
|
|
|
|
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert(value >= 0.0); |
|
|
|
|
|
if (value >= 1.0) { |
|
|
double wholePart; |
|
|
double wholePrecision; |
|
|
|
|
|
floatWhole(value, formattedP, &wholePart, &wholePrecision); |
|
|
|
|
|
if (wholePrecision >= 1.0) { |
|
|
|
|
|
|
|
|
|
|
|
} 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) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
|