@@ -316,12 +316,13 @@ static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t
316316
317317
318318#if defined(PRINTF_SUPPORT_FLOAT )
319- #include <math .h>
319+ #include <float .h>
320320#if defined(PRINTF_SUPPORT_EXPONENTIAL )
321321// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
322322static size_t _etoa (out_fct_type out , char * buffer , size_t idx , size_t maxlen , double value , unsigned int prec , unsigned int width , unsigned int flags );
323323#endif
324324
325+ // internal ftoa for fixed decimal floating point
325326static size_t _ftoa (out_fct_type out , char * buffer , size_t idx , size_t maxlen , double value , unsigned int prec , unsigned int width , unsigned int flags )
326327{
327328 char buf [PRINTF_FTOA_BUFFER_SIZE ];
@@ -452,6 +453,8 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d
452453}
453454
454455#if defined(PRINTF_SUPPORT_EXPONENTIAL )
456+ // internal ftoa variant for exponential floating-point type
457+ // contributed by Martijn Jasperse <m.jasperse@gmail.com>
455458static size_t _etoa (out_fct_type out , char * buffer , size_t idx , size_t maxlen , double value , unsigned int prec , unsigned int width , unsigned int flags )
456459{
457460 // check for special values
@@ -462,29 +465,49 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d
462465 bool negative = value < 0 ;
463466 if (negative ) value = - value ;
464467
465- // determine the decimal exponent
466- int expval = (int )floor (log10 (value )); // "value" must be +ve
467-
468- // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
469- unsigned int minwidth = ((expval < 100 )&& (expval > -100 )) ? 4 : 5 ;
470-
471468 // default precision
472469 if (!(flags & FLAGS_PRECISION )) {
473470 prec = PRINTF_DEFAULT_FLOAT_PRECISION ;
474471 }
475472
473+ // determine the decimal exponent
474+ // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
475+ union {
476+ uint64_t U ;
477+ double F ;
478+ } conv ;
479+ conv .F = value ;
480+ int exp2 = (int )((conv .U >> 52 ) & 0x07FF ) - 1023 ; // effectively log2
481+ conv .U = (conv .U & ((1ULL << 52 ) - 1 )) | (1023ULL << 52 ); // drop the exponent so conv.F is now in [1,2)
482+ // now approximate log10 from the log2 integer part and an expansion of ln around 1.5
483+ int expval = (int )(0.1760912590558 + exp2 * 0.301029995663981 + (conv .F - 1.5 )* 0.289529654602168 );
484+ // now we want to compute 10^expval but we want to be sure it won't overflow
485+ exp2 = (int )(expval * 3.321928094887362 + 0.5 );
486+ double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453 ;
487+ double z2 = z * z ;
488+ conv .U = (uint64_t )(exp2 + 1023 ) << 52 ;
489+ // compute exp(z) using continued fractions
490+ // https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
491+ conv .F *= 1 + 2 * z /(2 - z + (z2 /(6 + (z2 /(10 + z2 /14 )))));
492+ // correct for rounding errors
493+ if (value < conv .F ) {
494+ expval -- ;
495+ conv .F /= 10 ;
496+ }
497+
498+ // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
499+ unsigned int minwidth = ((expval < 100 )&& (expval > -100 )) ? 4 : 5 ;
500+
476501 // in "%g" mode, "prec" is the number of *significant figures* not decimals
477502 if (flags & FLAGS_ADAPT_EXP ) {
478- // do we want to fall-back to "%f" mode for small number ?
479- if ((expval > -5 )&& (expval < 6 )) {
503+ // do we want to fall-back to "%f" mode?
504+ if ((value >= 1e-4 )&& (value < 1e6 )) {
480505 if ((int )prec > expval ) {
481506 prec = (unsigned )((int )prec - expval - 1 );
482507 } else {
483508 prec = 0 ;
484509 }
485- // TODO: there's also a special case where we're supposed to ELIMINATE digits from the whole part
486- flags |= FLAGS_PRECISION ; // make sure _ftoa respects precision
487-
510+ flags |= FLAGS_PRECISION ; // make sure _ftoa respects precision
488511 // no characters in exponent
489512 minwidth = 0 ;
490513 expval = 0 ;
@@ -508,7 +531,7 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d
508531 }
509532
510533 // rescale the float value
511- if (expval ) value *= pow ( 10.0 , - expval ) ;
534+ if (expval ) value /= conv . F ;
512535
513536 // output the floating part
514537 const size_t start_idx = idx ;
0 commit comments