The objective was to write a locale-independent function that prints floating-point numbers (i.e. one that always uses . as a decimal point regardless of the locale) that was going to be used in a library that was meant to produce JSON. I was aiming for the following:
- Implementation simplicity
- Ability to be used on different sorts of streams (writing into a file, a string, etc)
- Reasonable corrrectness
Here's the main code:
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
typedef int (*printfn_t)(void *, const char *, ...);
#define print(...) if (printfn(stream, __VA_ARGS__) < 0) {\
return false;\
}
static const int PRECISION = 15;
static int count_digits(double n) {
return floor(log10(n) + 1);
}
bool print_number(
printfn_t printfn,
void *stream,
double number
) {
if (number == 0) {
print("0");
return true;
}
if (number < 0) {
print("-");
number = -number;
}
uint64_t integral = 0, decimal = 0;
int exponent = 0;
if (number > 9007199254740991) {
exponent = count_digits(number) - 1;
number = number / pow(10, exponent);
} else if (number < 1 / pow(10, PRECISION)) {
exponent = count_digits(number) - 1;
number = number * pow(10, -exponent);
}
integral = number;
decimal = round(fmod(number, 1) * pow(10, PRECISION));
print("%" PRId64, integral);
if (decimal != 0) {
print(".");
int decimal_length = count_digits(decimal);
for (int i = 0; i < PRECISION - decimal_length; i++) {
print("0");
}
print("%" PRId64, decimal);
}
if (exponent != 0) {
print("e%i", exponent);
}
return true;
}
And here's how you use it:
int main() {
double input = 0;
scanf("%lf", &input);
print_number(
(printfn_t) fprintf,
stdout,
input
);
}
LC_NUMERICto C locale (usesetlocale(LC_NUMERIC, NULL)to obtain the current locale to revert back to). \$\endgroup\$