|
|
|
@ -64,6 +64,21 @@
|
|
|
|
|
#define PRINTF_SUPPORT_FLOAT
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// support for exponential floating point notation (%e/%g)
|
|
|
|
|
#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
|
|
|
|
|
#define PRINTF_SUPPORT_EXPONENTIAL
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// define the default floating point precision
|
|
|
|
|
#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
|
|
|
|
|
#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// define the largest float suitable to print with %f
|
|
|
|
|
#ifndef PRINTF_MAX_FLOAT
|
|
|
|
|
#define PRINTF_MAX_FLOAT 1e9
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// support for the long long types (%llu or %p)
|
|
|
|
|
// default: activated
|
|
|
|
|
#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
|
|
|
|
@ -91,6 +106,7 @@
|
|
|
|
|
#define FLAGS_LONG (1U << 8U)
|
|
|
|
|
#define FLAGS_LONG_LONG (1U << 9U)
|
|
|
|
|
#define FLAGS_PRECISION (1U << 10U)
|
|
|
|
|
#define FLAGS_ADAPT_EXP (1U << 11U)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// output function type
|
|
|
|
@ -170,7 +186,6 @@ static unsigned int _atoi(const char** str)
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// internal itoa format
|
|
|
|
|
static 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)
|
|
|
|
|
{
|
|
|
|
@ -297,6 +312,11 @@ static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(PRINTF_SUPPORT_FLOAT)
|
|
|
|
|
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
|
|
|
|
|
// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
|
|
|
|
|
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);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static 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)
|
|
|
|
|
{
|
|
|
|
|
const size_t start_idx = idx;
|
|
|
|
@ -305,9 +325,6 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d
|
|
|
|
|
size_t len = 0U;
|
|
|
|
|
double diff = 0.0;
|
|
|
|
|
|
|
|
|
|
// if input is larger than thres_max, revert to exponential
|
|
|
|
|
const double thres_max = (double)0x7FFFFFFF;
|
|
|
|
|
|
|
|
|
|
// powers of 10
|
|
|
|
|
static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
|
|
|
|
|
|
|
|
|
@ -319,6 +336,16 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// test for very large values
|
|
|
|
|
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
|
|
|
|
|
if ((value > PRINTF_MAX_FLOAT)||(value < -PRINTF_MAX_FLOAT)) {
|
|
|
|
|
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
|
|
|
|
|
return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
|
|
|
|
|
#else
|
|
|
|
|
return 0U;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// test for negative
|
|
|
|
|
bool negative = false;
|
|
|
|
|
if (value < 0) {
|
|
|
|
@ -326,9 +353,9 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d
|
|
|
|
|
value = 0 - value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set default precision to 6, if not set explicitly
|
|
|
|
|
// set default precision, if not set explicitly
|
|
|
|
|
if (!(flags & FLAGS_PRECISION)) {
|
|
|
|
|
prec = 6U;
|
|
|
|
|
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
|
|
|
|
|
}
|
|
|
|
|
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
|
|
|
|
|
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
|
|
|
|
@ -356,12 +383,6 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d
|
|
|
|
|
++frac;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TBD: for very large numbers switch back to native sprintf for exponentials. Anyone want to write code to replace this?
|
|
|
|
|
// Normal printf behavior is to print EVERY whole number digit which can be 100s of characters overflowing your buffers == bad
|
|
|
|
|
if (value > thres_max) {
|
|
|
|
|
return 0U;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (prec == 0U) {
|
|
|
|
|
diff = value - (double)whole;
|
|
|
|
|
if (diff > 0.5) {
|
|
|
|
@ -445,6 +466,72 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d
|
|
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
|
|
|
|
|
#include <math.h>
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
// determine the sign
|
|
|
|
|
bool negative = value < 0;
|
|
|
|
|
if (negative) value = -value;
|
|
|
|
|
|
|
|
|
|
// determine the decimal exponent
|
|
|
|
|
int expval = (int)floor(log10(value)); // "value" must be +ve
|
|
|
|
|
|
|
|
|
|
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
|
|
|
|
|
unsigned int minwidth = ((expval < 100)&&(expval > -100)) ? 4 : 5;
|
|
|
|
|
|
|
|
|
|
// default precision
|
|
|
|
|
if (!(flags & FLAGS_PRECISION)) {
|
|
|
|
|
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// in "%g" mode, "prec" is the number of *significant figures* not decimals
|
|
|
|
|
if (flags & FLAGS_ADAPT_EXP) {
|
|
|
|
|
// do we want to fall-back to "%f" mode for small number?
|
|
|
|
|
if ((expval > -5)&&(expval < 6)) {
|
|
|
|
|
if ((int)prec > expval) {
|
|
|
|
|
prec = (unsigned)((int)prec - expval - 1);
|
|
|
|
|
} else {
|
|
|
|
|
prec = 0;
|
|
|
|
|
}
|
|
|
|
|
// TODO: there's also a special case where we're supposed to ELIMINATE digits from the whole part
|
|
|
|
|
flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
|
|
|
|
|
|
|
|
|
|
// no characters in exponent
|
|
|
|
|
minwidth = 0;
|
|
|
|
|
expval = 0;
|
|
|
|
|
} else {
|
|
|
|
|
// we use one sigfig for the whole part
|
|
|
|
|
if ((prec > 0)&&(flags & FLAGS_PRECISION)) --prec;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// will everything fit?
|
|
|
|
|
if (width > minwidth) {
|
|
|
|
|
// we didn't fall-back so subtract the characters required for the exponent
|
|
|
|
|
width -= minwidth;
|
|
|
|
|
} else {
|
|
|
|
|
// not enough characters, so go back to default sizing
|
|
|
|
|
width = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// rescale the float value
|
|
|
|
|
if (expval) value *= pow(10.0, -expval);
|
|
|
|
|
|
|
|
|
|
// output the floating part
|
|
|
|
|
idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, width, flags & ~FLAGS_ADAPT_EXP);
|
|
|
|
|
|
|
|
|
|
// output the exponent part
|
|
|
|
|
if (minwidth) {
|
|
|
|
|
// output the exponential symbol
|
|
|
|
|
out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
|
|
|
|
|
// output the exponent value
|
|
|
|
|
idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
|
|
|
|
|
}
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // PRINTF_SUPPORT_EXPONENTIAL
|
|
|
|
|
#endif // PRINTF_SUPPORT_FLOAT
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -632,9 +719,21 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const
|
|
|
|
|
#if defined(PRINTF_SUPPORT_FLOAT)
|
|
|
|
|
case 'f' :
|
|
|
|
|
case 'F' :
|
|
|
|
|
if (*format == 'F') flags |= FLAGS_UPPERCASE;
|
|
|
|
|
idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
|
|
|
|
|
format++;
|
|
|
|
|
break;
|
|
|
|
|
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
|
|
|
|
|
case 'e':
|
|
|
|
|
case 'E':
|
|
|
|
|
case 'g':
|
|
|
|
|
case 'G':
|
|
|
|
|
if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP;
|
|
|
|
|
if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE;
|
|
|
|
|
idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
|
|
|
|
|
format++;
|
|
|
|
|
break;
|
|
|
|
|
#endif // PRINTF_SUPPORT_EXPONENTIAL
|
|
|
|
|
#endif // PRINTF_SUPPORT_FLOAT
|
|
|
|
|
case 'c' : {
|
|
|
|
|
unsigned int l = 1U;
|
|
|
|
|