6464#define PRINTF_SUPPORT_FLOAT
6565#endif
6666
67+ // support for exponential floating point notation (%e/%g)
68+ #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
69+ #define PRINTF_SUPPORT_EXPONENTIAL
70+ #endif
71+
72+ // define the default floating point precision
73+ #ifndef PRINTF_DEFAULT_FLOAT_PRECISION
74+ #define PRINTF_DEFAULT_FLOAT_PRECISION 6U
75+ #endif
76+
77+ // define the largest float suitable to print with %f
78+ #ifndef PRINTF_MAX_FLOAT
79+ #define PRINTF_MAX_FLOAT 1e9
80+ #endif
81+
6782// support for the long long types (%llu or %p)
6883// default: activated
6984#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
91106#define FLAGS_LONG (1U << 8U)
92107#define FLAGS_LONG_LONG (1U << 9U)
93108#define FLAGS_PRECISION (1U << 10U)
109+ #define FLAGS_ADAPT_EXP (1U << 11U)
94110
95111
96112// output function type
@@ -169,12 +185,34 @@ static unsigned int _atoi(const char** str)
169185 return i ;
170186}
171187
188+ // output the specified string in reverse, taking care of any zero-padding
189+ static size_t _out_rev (out_fct_type out , char * buffer , size_t idx , size_t maxlen , const char * buf , size_t len , unsigned int width , unsigned int flags )
190+ {
191+ const size_t start_idx = idx ;
192+
193+ // pad spaces up to given width
194+ if (!(flags & FLAGS_LEFT ) && !(flags & FLAGS_ZEROPAD )) {
195+ for (size_t i = len ; i < width ; i ++ ) {
196+ out (' ' , buffer , idx ++ , maxlen );
197+ }
198+ }
199+
200+ // reverse string
201+ while (len ) out (buf [-- len ], buffer , idx ++ , maxlen );
202+
203+ // append pad spaces up to given width
204+ if (flags & FLAGS_LEFT ) {
205+ while (idx - start_idx < width ) {
206+ out (' ' , buffer , idx ++ , maxlen );
207+ }
208+ }
209+
210+ return idx ;
211+ }
172212
173213// internal itoa format
174214static size_t _ntoa_format (out_fct_type out , char * buffer , size_t idx , size_t maxlen , char * buf , size_t len , bool negative , unsigned int base , unsigned int prec , unsigned int width , unsigned int flags )
175215{
176- const size_t start_idx = idx ;
177-
178216 // pad leading zeros
179217 if (!(flags & FLAGS_LEFT )) {
180218 if (width && (flags & FLAGS_ZEROPAD ) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE )))) {
@@ -222,26 +260,7 @@ static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t ma
222260 }
223261 }
224262
225- // pad spaces up to given width
226- if (!(flags & FLAGS_LEFT ) && !(flags & FLAGS_ZEROPAD )) {
227- for (size_t i = len ; i < width ; i ++ ) {
228- out (' ' , buffer , idx ++ , maxlen );
229- }
230- }
231-
232- // reverse string
233- for (size_t i = 0U ; i < len ; i ++ ) {
234- out (buf [len - i - 1U ], buffer , idx ++ , maxlen );
235- }
236-
237- // append pad spaces up to given width
238- if (flags & FLAGS_LEFT ) {
239- while (idx - start_idx < width ) {
240- out (' ' , buffer , idx ++ , maxlen );
241- }
242- }
243-
244- return idx ;
263+ return _out_rev (out , buffer , idx , maxlen , buf , len , width , flags );
245264}
246265
247266
@@ -296,26 +315,38 @@ static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t
296315
297316
298317#if defined(PRINTF_SUPPORT_FLOAT )
318+ #include <float.h>
319+ #if defined(PRINTF_SUPPORT_EXPONENTIAL )
320+ // forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
321+ static 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 );
322+ #endif
323+
324+ // internal ftoa for fixed decimal floating point
299325static 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 )
300326{
301- const size_t start_idx = idx ;
302-
303327 char buf [PRINTF_FTOA_BUFFER_SIZE ];
304328 size_t len = 0U ;
305329 double diff = 0.0 ;
306330
307- // if input is larger than thres_max, revert to exponential
308- const double thres_max = (double )0x7FFFFFFF ;
309-
310331 // powers of 10
311332 static const double pow10 [] = { 1 , 10 , 100 , 1000 , 10000 , 100000 , 1000000 , 10000000 , 100000000 , 1000000000 };
312333
313- // test for NaN
314- if (value != value ) {
315- out ('n' , buffer , idx ++ , maxlen );
316- out ('a' , buffer , idx ++ , maxlen );
317- out ('n' , buffer , idx ++ , maxlen );
318- return idx ;
334+ // test for special values
335+ if (value != value )
336+ return _out_rev (out , buffer , idx , maxlen , "nan" , 3 , width , flags );
337+ if (value < - DBL_MAX )
338+ return _out_rev (out , buffer , idx , maxlen , "fni-" , 4 , width , flags );
339+ if (value > DBL_MAX )
340+ return _out_rev (out , buffer , idx , maxlen , (flags & FLAGS_PLUS ) ? "fni+" : "fni" , (flags & FLAGS_PLUS ) ? 4 : 3 , width , flags );
341+
342+ // test for very large values
343+ // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
344+ if ((value > PRINTF_MAX_FLOAT )|| (value < - PRINTF_MAX_FLOAT )) {
345+ #if defined(PRINTF_SUPPORT_EXPONENTIAL )
346+ return _etoa (out , buffer , idx , maxlen , value , prec , width , flags );
347+ #else
348+ return 0U ;
349+ #endif
319350 }
320351
321352 // test for negative
@@ -325,9 +356,9 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d
325356 value = 0 - value ;
326357 }
327358
328- // set default precision to 6 , if not set explicitly
359+ // set default precision, if not set explicitly
329360 if (!(flags & FLAGS_PRECISION )) {
330- prec = 6U ;
361+ prec = PRINTF_DEFAULT_FLOAT_PRECISION ;
331362 }
332363 // limit precision to 9, cause a prec >= 10 can lead to overflow errors
333364 while ((len < PRINTF_FTOA_BUFFER_SIZE ) && (prec > 9U )) {
@@ -355,12 +386,6 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d
355386 ++ frac ;
356387 }
357388
358- // TBD: for very large numbers switch back to native sprintf for exponentials. Anyone want to write code to replace this?
359- // Normal printf behavior is to print EVERY whole number digit which can be 100s of characters overflowing your buffers == bad
360- if (value > thres_max ) {
361- return 0U ;
362- }
363-
364389 if (prec == 0U ) {
365390 diff = value - (double )whole ;
366391 if ((!(diff < 0.5 ) || (diff > 0.5 )) && (whole & 1 )) {
@@ -419,27 +444,109 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d
419444 }
420445 }
421446
422- // pad spaces up to given width
423- if (!(flags & FLAGS_LEFT ) && !(flags & FLAGS_ZEROPAD )) {
424- for (size_t i = len ; i < width ; i ++ ) {
425- out (' ' , buffer , idx ++ , maxlen );
447+ return _out_rev (out , buffer , idx , maxlen , buf , len , width , flags );
448+ }
449+
450+ #if defined(PRINTF_SUPPORT_EXPONENTIAL )
451+ // internal ftoa variant for exponential floating-point type
452+ // contributed by Martijn Jasperse <m.jasperse@gmail.com>
453+ static 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 )
454+ {
455+ // check for special values
456+ if ((value != value )|| (value > DBL_MAX )|| (value < - DBL_MAX ))
457+ return _ftoa (out , buffer , idx , maxlen , value , prec , width , flags );
458+
459+ // determine the sign
460+ bool negative = value < 0 ;
461+ if (negative ) value = - value ;
462+
463+ // default precision
464+ if (!(flags & FLAGS_PRECISION )) {
465+ prec = PRINTF_DEFAULT_FLOAT_PRECISION ;
466+ }
467+
468+ // determine the decimal exponent
469+ // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
470+ union {
471+ uint64_t U ;
472+ double F ;
473+ } conv ;
474+ conv .F = value ;
475+ int exp2 = (int )((conv .U >> 52 ) & 0x07FF ) - 1023 ; // effectively log2
476+ conv .U = (conv .U & ((1ULL << 52 ) - 1 )) | (1023ULL << 52 ); // drop the exponent so conv.F is now in [1,2)
477+ // now approximate log10 from the log2 integer part and an expansion of ln around 1.5
478+ int expval = (int )(0.1760912590558 + exp2 * 0.301029995663981 + (conv .F - 1.5 )* 0.289529654602168 );
479+ // now we want to compute 10^expval but we want to be sure it won't overflow
480+ exp2 = (int )(expval * 3.321928094887362 + 0.5 );
481+ double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453 ;
482+ double z2 = z * z ;
483+ conv .U = (uint64_t )(exp2 + 1023 ) << 52 ;
484+ // compute exp(z) using continued fractions
485+ // https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
486+ conv .F *= 1 + 2 * z /(2 - z + (z2 /(6 + (z2 /(10 + z2 /14 )))));
487+ // correct for rounding errors
488+ if (value < conv .F ) {
489+ expval -- ;
490+ conv .F /= 10 ;
491+ }
492+
493+ // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
494+ unsigned int minwidth = ((expval < 100 )&& (expval > -100 )) ? 4 : 5 ;
495+
496+ // in "%g" mode, "prec" is the number of *significant figures* not decimals
497+ if (flags & FLAGS_ADAPT_EXP ) {
498+ // do we want to fall-back to "%f" mode?
499+ if ((value >= 1e-4 )&& (value < 1e6 )) {
500+ if ((int )prec > expval ) {
501+ prec = (unsigned )((int )prec - expval - 1 );
502+ } else {
503+ prec = 0 ;
504+ }
505+ flags |= FLAGS_PRECISION ; // make sure _ftoa respects precision
506+ // no characters in exponent
507+ minwidth = 0 ;
508+ expval = 0 ;
509+ } else {
510+ // we use one sigfig for the whole part
511+ if ((prec > 0 )&& (flags & FLAGS_PRECISION )) -- prec ;
426512 }
427513 }
428-
429- // reverse string
430- for (size_t i = 0U ; i < len ; i ++ ) {
431- out (buf [len - i - 1U ], buffer , idx ++ , maxlen );
514+ // will everything fit?
515+ unsigned int fwidth = width ;
516+ if (width > minwidth ) {
517+ // we didn't fall-back so subtract the characters required for the exponent
518+ fwidth -= minwidth ;
519+ } else {
520+ // not enough characters, so go back to default sizing
521+ fwidth = 0 ;
522+ }
523+ if ((flags & FLAGS_LEFT ) && minwidth ) {
524+ // if we're padding on the right, DON'T pad the floating part
525+ fwidth = 0 ;
432526 }
433527
434- // append pad spaces up to given width
435- if (flags & FLAGS_LEFT ) {
436- while (idx - start_idx < width ) {
437- out (' ' , buffer , idx ++ , maxlen );
528+ // rescale the float value
529+ if (expval ) value /= conv .F ;
530+
531+ // output the floating part
532+ const size_t start_idx = idx ;
533+ idx = _ftoa (out , buffer , idx , maxlen , negative ? - value : value , prec , fwidth , flags & ~FLAGS_ADAPT_EXP );
534+
535+ // output the exponent part
536+ if (minwidth ) {
537+ // output the exponential symbol
538+ out ((flags & FLAGS_UPPERCASE ) ? 'E' : 'e' , buffer , idx ++ , maxlen );
539+ // output the exponent value
540+ idx = _ntoa_long (out , buffer , idx , maxlen , (expval < 0 ) ? - expval : expval , expval < 0 , 10 , 0 , minwidth - 1 , FLAGS_ZEROPAD | FLAGS_PLUS );
541+ // might need to right-pad spaces
542+ if (flags & FLAGS_LEFT ) {
543+ while (idx - start_idx < width ) out (' ' , buffer , idx ++ , maxlen );
438544 }
439545 }
440-
441546 return idx ;
442547}
548+
549+ #endif // PRINTF_SUPPORT_EXPONENTIAL
443550#endif // PRINTF_SUPPORT_FLOAT
444551
445552
@@ -627,9 +734,21 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const
627734#if defined(PRINTF_SUPPORT_FLOAT )
628735 case 'f' :
629736 case 'F' :
737+ if (* format == 'F' ) flags |= FLAGS_UPPERCASE ;
630738 idx = _ftoa (out , buffer , idx , maxlen , va_arg (va , double ), precision , width , flags );
631739 format ++ ;
632740 break ;
741+ #if defined(PRINTF_SUPPORT_EXPONENTIAL )
742+ case 'e' :
743+ case 'E' :
744+ case 'g' :
745+ case 'G' :
746+ if ((* format == 'g' )|| (* format == 'G' )) flags |= FLAGS_ADAPT_EXP ;
747+ if ((* format == 'E' )|| (* format == 'G' )) flags |= FLAGS_UPPERCASE ;
748+ idx = _etoa (out , buffer , idx , maxlen , va_arg (va , double ), precision , width , flags );
749+ format ++ ;
750+ break ;
751+ #endif // PRINTF_SUPPORT_EXPONENTIAL
633752#endif // PRINTF_SUPPORT_FLOAT
634753 case 'c' : {
635754 unsigned int l = 1U ;
0 commit comments