Locale-independent float print function 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 &lt;inttypes.h&gt;
#include &lt;math.h&gt;
#include &lt;stdbool.h&gt;
#include &lt;stdio.h&gt;

typedef int (*printfn_t)(void *, const char *, ...);

#define print(...) if (printfn(stream, __VA_ARGS__) &lt; 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(&quot;0&quot;);
        
        return true;
    }
    
    if (number &lt; 0) {
        print(&quot;-&quot;);
        
        number = -number;
    }
    
    uint64_t integral = 0, decimal = 0;
    int exponent = 0;
    
    if (number &gt; 9007199254740991) {
        exponent = count_digits(number) - 1;
        
        number = number / pow(10, exponent);
    } else if (number &lt; 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(&quot;%&quot; PRId64, integral);
    
    if (decimal != 0) {
        print(&quot;.&quot;);
        
        int decimal_length = count_digits(decimal);
        
        for (int i = 0; i &lt; PRECISION - decimal_length; i++) {
            print(&quot;0&quot;);
        }
        
        print(&quot;%&quot; PRId64, decimal);
    }
    
    if (exponent != 0) {
        print(&quot;e%i&quot;, exponent);
    }
    
    return true;
}

And here's how you use it:
int main() {
    double input = 0;
    
    scanf(&quot;%lf&quot;, &amp;input);
    
    print_number(
        (printfn_t) fprintf,
        stdout,
        input
    );
}
Implementation simplicity
As code is incorrect in too many cases, simplicity assessment is moot.
Ability to be used on different sorts of streams (writing into a file, a string, etc)
Code assumes it can pass a void * like a FILE *.  Reasonable, but not specified by C.
Writing to a string deserves a size parameter.
Rather than attempt to use fprintf(), sprintf(), consider providing interface functions.
Reasonable corrrectness
corrrectness --&gt;  correctness
OP is brave.  Quality conversion of a string to a double is hard.
To assess, I set up a test harness.
void test(double x) {
  // Reference output
  printf(&quot;R%.*e %20a\n&quot;, DBL_DECIMAL_DIG - 1, x, x);
  // OP's output
  printf(&quot;&lt;&quot;);
  print_number((printfn_t) fprintf, stdout, x);
  puts(&quot;&gt;\n&quot;);
}

int main(void) {
  const double x[] = {1.0, 1.0 / 7, DBL_MIN, DBL_TRUE_MIN, DBL_MAX, INFINITY,
      NAN, -DBL_MAX, -0.0, 1.0 + DBL_EPSILON, 1.0 - DBL_EPSILON};
  size_t n = sizeof x / sizeof *x;
  for (size_t i = 0; i &lt; n; i++) {
    test(x[i]);
  }
}

Outright discrepancies included
R4.9406564584124654e-324            0x1p-1074
&lt;0.-9223372036854775808e-324&gt;                          ???

Rinf                  inf
&lt;-9223372036854775808.-9223372036854775808e2147483647&gt; ???

Rnan                  nan
&lt;-9223372036854775808.-9223372036854775808&gt;            ???

R-0.0000000000000000e+00              -0x0p+0
&lt;0&gt;                                                    Missing sign

R9.9999999999999978e-01 0x1.ffffffffffffep-1
&lt;0.1000000000000000&gt;                                   Off by 10x!

Non-finite
For NAN and infinity, consider a !isfinite(x) test and then handle those special cases.
Sign
For -0.0 correctness,
//if (number &lt; 0) {
if (signbit(number)) {
    print(&quot;-&quot;);
    ...

Precision
double deserves up to DBL_DECIMAL_DIG (e.g. 17) places of output to properly distinguish all double from other double.  OP's precisions output wobbles with inconsistent significant precision output.
I adjusted OP's code to int PRECISION = DBL_DECIMAL_DIG;, then used select values like 1.0 - DBL_EPSILON (and many others) are not quite correct.
R9.9999999999999978e-01 0x1.ffffffffffffep-1
&lt;0.99999999999999984&gt;

As a reference, to print the exact value of a finite double. See Function to print a double - exactly
Numeric correctness.
Each *, / and some pow(10, ) inject 0.5 [ULP] or more error into the math and there is little code compensating for these accumulating errors.  Without internal extended math, I see OP's approach as unable to achieve a high quality result.
Code assumes double precision &lt; 64 bits.  Reasonable, but not specified by C.

Recommendation
Test with the usual suspects as in the test harness, with values near powers of 10 and with various random values.