From 87d27ef465caa7a743b85e6e4a8f4475f480fc8b Mon Sep 17 00:00:00 2001 From: Petr Ledvina Date: Mon, 13 May 2019 19:41:20 +0200 Subject: [PATCH 01/11] Replace padding loops with function --- printf.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/printf.c b/printf.c index 8a700add..60f8c9b3 100644 --- a/printf.c +++ b/printf.c @@ -194,17 +194,23 @@ static unsigned int _atoi(const char** str) return i; } +// output repeated character for padding. Return updated idx +// nothing is output if count is negative +static size_t _out_pad(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char padchar, int count) +{ + while (count-- > 0) { + out(padchar, buffer, idx++, maxlen); + } + return idx; +} // output the specified string in reverse, taking care of any zero-padding 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) { - const size_t start_idx = idx; - + const int pad = width - len; // pad spaces up to given width if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (size_t i = len; i < width; i++) { - out(' ', buffer, idx++, maxlen); - } + idx = _out_pad(out, buffer, idx, maxlen, ' ', pad); } // reverse string @@ -214,9 +220,7 @@ static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen // append pad spaces up to given width if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) { - out(' ', buffer, idx++, maxlen); - } + idx = _out_pad(out, buffer, idx, maxlen, ' ', pad); } return idx; @@ -564,7 +568,7 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); // might need to right-pad spaces if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + idx = _out_pad(out, buffer, idx, maxlen, ' ', width - (idx - start_idx)); } } return idx; @@ -774,20 +778,15 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const #endif // PRINTF_SUPPORT_EXPONENTIAL #endif // PRINTF_SUPPORT_FLOAT case 'c' : { - unsigned int l = 1U; // pre padding if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } + idx = _out_pad(out, buffer, idx, maxlen, ' ', (int)width - 1); } // char output out((char)va_arg(va, int), buffer, idx++, maxlen); // post padding if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } + idx = _out_pad(out, buffer, idx, maxlen, ' ', (int)width - 1); } format++; break; From ff1c0b891472c28f1da33f0314825d0ec8f06883 Mon Sep 17 00:00:00 2001 From: Petr Ledvina Date: Mon, 13 May 2019 19:47:23 +0200 Subject: [PATCH 02/11] Simplify/fix integer zero padding + hash flag Handle 0x / 0b when width is equal to string size (issue #50): printf("%#4x", 0x1234); -> "0x1234" Do not print # prefix if it does not fit into PRINTF_NTOA_BUFFER_SIZE %#o is implemented as in libc: - printf("%#0o", 0); does output octal prefix ("0") - printf("%#3o", 1); printf "001" - padding zero is used as octal prefix (issue #53) Left padding and precision is handled correctly (issue #49): - printf("%-10.6d", 1024); -> "001024 " --- printf.c | 72 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/printf.c b/printf.c index 60f8c9b3..e9be95e2 100644 --- a/printf.c +++ b/printf.c @@ -231,40 +231,46 @@ static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen 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) { // pad leading zeros - if (!(flags & FLAGS_LEFT)) { - if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; + int zeropad = 0; + if (flags & FLAGS_PRECISION) { + // no other source of zero-padding if prec is specified + zeropad = (int)(prec - len); } + else if (flags & FLAGS_ZEROPAD) { + zeropad = (int)(width - len); + if (negative || flags & (FLAGS_PLUS | FLAGS_SPACE)) { + // keep one position for sign + zeropad--; } - - // handle hash if (flags & FLAGS_HASH) { - if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { - len--; - if (len && (base == 16U)) { - len--; + // keep space for 0x / 0b + // octal is handled separately + if (base == 16U || base == 2U) { + zeropad -= 2; } } - if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'x'; } - else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'X'; + + while ((zeropad-- > 0) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; } - else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + + // handle hash + if (flags & FLAGS_HASH) { + if (base == 2 && len < PRINTF_NTOA_BUFFER_SIZE - 1) { buf[len++] = 'b'; + buf[len++] = '0'; } - if (len < PRINTF_NTOA_BUFFER_SIZE) { + // pesky octal case. Add 0 prefix only if following digit is not zero (both from value and zero-padding) + if (base == 8 && len < PRINTF_NTOA_BUFFER_SIZE && (!len || buf[len - 1] != '0')) { buf[len++] = '0'; } + if (base == 16 && len < PRINTF_NTOA_BUFFER_SIZE - 1) { + buf[len++] = (flags & FLAGS_UPPERCASE) ? 'X' : 'x'; + buf[len++] = '0'; } - + } + // handle '+', '-', ' ' if (len < PRINTF_NTOA_BUFFER_SIZE) { if (negative) { buf[len++] = '-'; @@ -287,12 +293,13 @@ static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxl char buf[PRINTF_NTOA_BUFFER_SIZE]; size_t len = 0U; - // no hash for 0 values - if (!value) { + // no hash for 0 values, except octal 0 */ + if (!value && (base != 8)) { flags &= ~FLAGS_HASH; } - // write if precision != 0 and value is != 0 + // if precision is specified and zero, don't print zero value + // if precision > 0, zero padding code will supply at least one zero if (!(flags & FLAGS_PRECISION) || value) { do { const char digit = (char)(value % base); @@ -313,11 +320,12 @@ static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t size_t len = 0U; // no hash for 0 values - if (!value) { + if (!value && (base != 8)) { flags &= ~FLAGS_HASH; } - // write if precision != 0 and value is != 0 + // if precision is specified and zero, don't print zero value + // if precision > 0, zero padding code will supply at least one zero if (!(flags & FLAGS_PRECISION) || value) { do { const char digit = (char)(value % base); @@ -717,8 +725,8 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const flags &= ~(FLAGS_PLUS | FLAGS_SPACE); } - // ignore '0' flag when precision is given - if (flags & FLAGS_PRECISION) { + // ignore '0' flag when precision is given or padding to left + if (flags & (FLAGS_PRECISION | FLAGS_LEFT)) { flags &= ~FLAGS_ZEROPAD; } @@ -761,6 +769,9 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const #if defined(PRINTF_SUPPORT_FLOAT) case 'f' : case 'F' : + if (flags & FLAGS_LEFT) { + flags &= ~FLAGS_ZEROPAD; + } if (*format == 'F') flags |= FLAGS_UPPERCASE; idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); format++; @@ -770,6 +781,9 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const case 'E': case 'g': case 'G': + if (flags & FLAGS_LEFT) { + flags &= ~FLAGS_ZEROPAD; + } 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); From 2706024b64a844fee069bc48a47643a7a40a278e Mon Sep 17 00:00:00 2001 From: Petr Ledvina Date: Mon, 13 May 2019 20:06:32 +0200 Subject: [PATCH 03/11] Fix/improve %f rounding code Fixes issue #51 - fractional part may overflow in round-to-even part Rounding is still inexact, some numbers are rounded wrong way --- printf.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/printf.c b/printf.c index e9be95e2..6e4d52c6 100644 --- a/printf.c +++ b/printf.c @@ -397,30 +397,27 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d unsigned long frac = (unsigned long)tmp; diff = tmp - frac; - if (diff > 0.5) { + if (diff < 0.5) { + // round down + } + else if (diff > 0.5) { + // round up ++frac; + } + else { + // half-way, round to even + if (prec == 0U) { + whole = (whole + 1) & ~1U; // round whole to even + } else { + frac = (frac + 1) & ~1U; // round frac to even + } + } // handle rollover, e.g. case 0.99 with prec 1 is 1.0 if (frac >= pow10[prec]) { frac = 0; ++whole; } - } - else if (diff < 0.5) { - } - else if ((frac == 0U) || (frac & 1U)) { - // if halfway, round up if odd OR if last digit is 0 - ++frac; - } - if (prec == 0U) { - diff = value - (double)whole; - if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { - // exactly 0.5 and ODD, then round up - // 1.5 -> 2, but 2.5 -> 2 - ++whole; - } - } - else { unsigned int count = prec; // now do fractional part, as an unsigned number while (len < PRINTF_FTOA_BUFFER_SIZE) { From 4163121848cd770896ead3cf419beefc8b3698c3 Mon Sep 17 00:00:00 2001 From: Petr Ledvina Date: Mon, 13 May 2019 20:11:39 +0200 Subject: [PATCH 04/11] Implement %#.0f printf("%#.0f", 1) -> "1." --- printf.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/printf.c b/printf.c index 6e4d52c6..e442b131 100644 --- a/printf.c +++ b/printf.c @@ -418,6 +418,7 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d ++whole; } + if (prec > 0U) { unsigned int count = prec; // now do fractional part, as an unsigned number while (len < PRINTF_FTOA_BUFFER_SIZE) { @@ -436,6 +437,12 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d buf[len++] = '.'; } } + else if (flags & FLAGS_HASH) { + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal point if precision is zero and hash flag is set + buf[len++] = '.'; + } + } // do whole part, number is reversed while (len < PRINTF_FTOA_BUFFER_SIZE) { From 9a40aa2c7158a4f0d1e98d30d5c00b8b55a0117b Mon Sep 17 00:00:00 2001 From: Petr Ledvina Date: Mon, 13 May 2019 20:16:35 +0200 Subject: [PATCH 05/11] Minor cleanup of _ftoa code --- printf.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/printf.c b/printf.c index e442b131..ea706044 100644 --- a/printf.c +++ b/printf.c @@ -376,9 +376,8 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d } // test for negative - bool negative = false; - if (value < 0) { - negative = true; + bool negative = value < 0; + if (negative) { value = 0 - value; } @@ -392,9 +391,10 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d prec--; } - int whole = (int)value; - double tmp = (value - whole) * pow10[prec]; - unsigned long frac = (unsigned long)tmp; + // use signed value for double/int conversions, some CPUs don't support unsigned conversion, making the code larger + unsigned long whole = (unsigned long)(long)value; + double tmp = (value - (long)whole) * pow10[prec]; + unsigned long frac = (unsigned long)(long)tmp; diff = tmp - frac; if (diff < 0.5) { @@ -421,12 +421,11 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d if (prec > 0U) { unsigned int count = prec; // now do fractional part, as an unsigned number - while (len < PRINTF_FTOA_BUFFER_SIZE) { + // digits(frac) <= prec + while (len < PRINTF_FTOA_BUFFER_SIZE && frac) { --count; - buf[len++] = (char)(48U + (frac % 10U)); - if (!(frac /= 10U)) { - break; - } + buf[len++] = (char)('0' + (frac % 10U)); + frac /= 10U; } // add extra 0s while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { @@ -446,8 +445,8 @@ static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d // do whole part, number is reversed while (len < PRINTF_FTOA_BUFFER_SIZE) { - buf[len++] = (char)(48 + (whole % 10)); - if (!(whole /= 10)) { + buf[len++] = (char)('0' + (whole % 10U)); + if (!(whole /= 10)) { // output at least one zero break; } } From c6b98665984583522ee2cf87a5436911f5a9ea7a Mon Sep 17 00:00:00 2001 From: Petr Ledvina Date: Mon, 13 May 2019 20:18:36 +0200 Subject: [PATCH 06/11] Print 0 as 0.0 / 0.0e+00 Old code did use -308 as exponent for 0.0, now value is printed with zero exponent --- printf.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/printf.c b/printf.c index ea706044..efefd259 100644 --- a/printf.c +++ b/printf.c @@ -503,12 +503,14 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d uint64_t U; double F; } conv; + int expval; + if (value != 0) { conv.F = value; int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 - int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); // now we want to compute 10^expval but we want to be sure it won't overflow exp2 = (int)(expval * 3.321928094887362 + 0.5); const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; @@ -521,6 +523,12 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d expval--; conv.F /= 10; } + } + else { + // treat zero as 0.0e0 + conv.F = 0; + expval = 0; + } // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; From fc333898cf71c545d5cc42490703ed53e9ecc9a0 Mon Sep 17 00:00:00 2001 From: Petr Ledvina Date: Mon, 13 May 2019 20:21:14 +0200 Subject: [PATCH 07/11] Fix precision handling in %g - %g precision is number of significant digits. Default %g precision shall be honored when printing as %f printf("%g", 123.4567); -> "123.457" - %g prints zero correctly (except for trailing zeroes) printf("%g", 0); -> "0.0" - %g shall switch to %f if supplied value fits into specified precision printf("%.8g", 12345678); -> "12345678" --- printf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/printf.c b/printf.c index efefd259..999e68a5 100644 --- a/printf.c +++ b/printf.c @@ -535,22 +535,24 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d // in "%g" mode, "prec" is the number of *significant figures* not decimals if (flags & FLAGS_ADAPT_EXP) { + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision // do we want to fall-back to "%f" mode? - if ((value >= 1e-4) && (value < 1e6)) { + // check if value is between 1e-4 and 1e6 or it can be printed with precision digits (and within ftoa limit) + // handles 0.0 too (exp is 0) + if ((expval >= -4) && ((expval < 6) || ((expval < (int)prec) && (expval <= 9)))) { if ((int)prec > expval) { prec = (unsigned)((int)prec - expval - 1); } else { prec = 0; } - flags |= FLAGS_PRECISION; // make sure _ftoa respects precision // no characters in exponent minwidth = 0U; expval = 0; } else { - // we use one sigfig for the whole part - if ((prec > 0) && (flags & FLAGS_PRECISION)) { + // we use one sigfig for the whole part (even with default precision) + if (prec > 0) { --prec; } } From e88e74153fa6dd05c7a411232b320be6f765cb96 Mon Sep 17 00:00:00 2001 From: Petr Ledvina Date: Mon, 13 May 2019 20:29:52 +0200 Subject: [PATCH 08/11] Simplify flags handling a bit --- printf.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/printf.c b/printf.c index 999e68a5..a3a28e6e 100644 --- a/printf.c +++ b/printf.c @@ -601,7 +601,7 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d // internal vsnprintf static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) { - unsigned int flags, width, precision, n; + unsigned int flags, width, precision; size_t idx = 0U; if (!buffer) { @@ -625,16 +625,16 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const // evaluate flags flags = 0U; - do { + for (bool cont = true; cont; ) { switch (*format) { - case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; - case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; - case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; - case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; - case '#': flags |= FLAGS_HASH; format++; n = 1U; break; - default : n = 0U; break; + case '0': flags |= FLAGS_ZEROPAD; format++; break; + case '-': flags |= FLAGS_LEFT; format++; break; + case '+': flags |= FLAGS_PLUS; format++; break; + case ' ': flags |= FLAGS_SPACE; format++; break; + case '#': flags |= FLAGS_HASH; format++; break; + default : cont = 0U; break; + } } - } while (n); // evaluate width field width = 0U; From f5116030e86bd6c90fec8b471b7b6b3e3a65dac7 Mon Sep 17 00:00:00 2001 From: Petr Ledvina Date: Mon, 13 May 2019 20:30:50 +0200 Subject: [PATCH 09/11] Handle negaive precision correctly Negative value passed by "%.*" shall be taken as if no precision was specified --- printf.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/printf.c b/printf.c index a3a28e6e..64f2f898 100644 --- a/printf.c +++ b/printf.c @@ -663,7 +663,12 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const } else if (*format == '*') { const int prec = (int)va_arg(va, int); - precision = prec > 0 ? (unsigned int)prec : 0U; + if (prec >= 0) { + precision = (unsigned)prec; + } else { + // negative precision is like no precision specifier at all + flags &= ~FLAGS_PRECISION; + } format++; } } From 184e14aaa12c31cbdb2c4a22be29b6fe86b45c26 Mon Sep 17 00:00:00 2001 From: Petr Ledvina Date: Mon, 13 May 2019 20:34:20 +0200 Subject: [PATCH 10/11] Simplify %s a bit uses _out_pad, removes some unnecessary tests (length of string to print is known) --- printf.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/printf.c b/printf.c index 64f2f898..832c6e2e 100644 --- a/printf.c +++ b/printf.c @@ -826,25 +826,18 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const case 's' : { const char* p = va_arg(va, char*); - unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + unsigned int toprint = _strnlen_s(p, (flags & FLAGS_PRECISION) ? precision : (size_t)-1); // pre padding - if (flags & FLAGS_PRECISION) { - l = (l < precision ? l : precision); - } if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } + idx = _out_pad(out, buffer, idx, maxlen, ' ', (int)(width - toprint)); } // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { - out(*(p++), buffer, idx++, maxlen); + for (unsigned int i = 0; i < toprint; i++) { + out(p[i], buffer, idx++, maxlen); } // post padding if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } + idx = _out_pad(out, buffer, idx, maxlen, ' ', (int)(width - toprint)); } format++; break; From e29f0f83e3829c41ed071aceefb71edd876e11ca Mon Sep 17 00:00:00 2001 From: Petr Ledvina Date: Mon, 13 May 2019 20:35:58 +0200 Subject: [PATCH 11/11] Fix signed value overflow Signed overflow is undefined in C, this was causing problem when printing INT_MIN (gcc sign-extended value first, so 2^64-INT_MIN got printed) --- printf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/printf.c b/printf.c index 832c6e2e..682a79d3 100644 --- a/printf.c +++ b/printf.c @@ -586,7 +586,7 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d // 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); + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? 0LU - (unsigned long)expval : (unsigned long)expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); // might need to right-pad spaces if (flags & FLAGS_LEFT) { idx = _out_pad(out, buffer, idx, maxlen, ' ', width - (idx - start_idx)); @@ -754,16 +754,18 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const if (flags & FLAGS_LONG_LONG) { #if defined(PRINTF_SUPPORT_LONG_LONG) const long long value = va_arg(va, long long); - idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (value >= 0 ? (unsigned long long)value : 0ULL - (unsigned long long)value), + value < 0, base, precision, width, flags); #endif } else if (flags & FLAGS_LONG) { const long value = va_arg(va, long); - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + // -LONG_MIN is undefined in C (overflow), convert to unsigned first + idx = _ntoa_long(out, buffer, idx, maxlen, (value >= 0 ? (unsigned long)value : 0UL - (unsigned long)value), value < 0, base, precision, width, flags); } else { const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + idx = _ntoa_long(out, buffer, idx, maxlen, (value >= 0 ? (unsigned int)value : 0U - (unsigned int)value), value < 0, base, precision, width, flags); } } else {