From 8946e0a9021727b3a2b14d04a05f98ffabcf5a7a Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Wed, 4 Aug 2021 23:02:57 +0300 Subject: [PATCH] Partial merge of `_etoa` and `_ftoa`: We now have a single `sprint_floating_point()` function, which performs the common work and allocates the temporary buffer necessary storing the reversed digits. It then calls one of two functions - but these are not quote like `ftoa()` and `etoa()` in that they can make stronger assumptions about their input and can skip some of the work. Also, some renaming. --- printf.c | 535 +++++++++++++++++++++++++++---------------------------- 1 file changed, 266 insertions(+), 269 deletions(-) diff --git a/printf.c b/printf.c index 335b7919..4adc034d 100644 --- a/printf.c +++ b/printf.c @@ -97,6 +97,9 @@ #define NTOA_VALUE_TYPE unsigned long #endif +#define PRINTF_PREFER_DECIMAL false +#define PRINTF_PREFER_EXPONENTIAL true + /////////////////////////////////////////////////////////////////////////////// // The following will convert the number-of-digits into an exponential-notation literal @@ -343,17 +346,11 @@ static size_t _ntoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, N return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, base, precision, width, flags); } - #if PRINTF_SUPPORT_FLOAT_SPECIFIERS // Note: This assumes _x is a _number - not NaN nor +/- infinity #define SIGN_OF_DOUBLE_NUMBER(_x) (*(const uint64_t *)(&(_x)) >> 63U) -#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS -// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_FLOAT_NOTATION_THRESHOLD -static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int precision, unsigned int width, unsigned int flags); -#endif - struct double_components { int_fast64_t integral; int_fast64_t fractional; @@ -405,8 +402,7 @@ static struct double_components get_components(double number, unsigned int preci return number_; } -static size_t -sprint_broken_up_decimal( +static size_t sprint_broken_up_decimal( struct double_components number_, out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned int precision, unsigned int width, unsigned int flags, char *buf, size_t len) { @@ -485,113 +481,70 @@ sprint_broken_up_decimal( } // internal ftoa for fixed decimal floating point -static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int precision, unsigned int width, unsigned int flags) +static size_t sprint_decimal_number(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double number, unsigned int precision, unsigned int width, unsigned int flags, char* buf, size_t len) { - char buf[PRINTF_FTOA_BUFFER_SIZE]; - size_t len = 0U; - - // test for special values - if (value != value) - return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); - if (value < -DBL_MAX) - return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); - if (value > DBL_MAX) - return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); - - // test for very large values - // standard printf behavior is to print EVERY integral-part digit -- which could be 100s of characters overflowing your buffers == bad - if ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD)) { -#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - return _etoa(out, buffer, idx, maxlen, value, precision, width, flags); -#else - return 0U; -#endif - } - - // set default precision, if not set explicitly - if (!(flags & FLAGS_PRECISION)) { - precision = PRINTF_DEFAULT_FLOAT_PRECISION; - } - -#define NUM_DECIMAL_DIGITS_IN_INT64_T 18U - // limit precision to 9, cause a precision >= 10 can lead to overflow errors - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (precision > NUM_DECIMAL_DIGITS_IN_INT64_T)) { - buf[len++] = '0'; // This respects the precision in terms of result length only - precision--; - } - - struct double_components value_ = get_components(value, precision); + struct double_components value_ = get_components(number, precision); return sprint_broken_up_decimal(value_, out, buffer, idx, maxlen, precision, width, flags, buf, len); } + #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS // internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse -static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int precision, unsigned int width, unsigned int flags) +static size_t sprint_exponential_number(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double number, unsigned int precision, unsigned int width, unsigned int flags, char* buf, size_t len) { - // check for NaN and special values - if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { - return _ftoa(out, buffer, idx, maxlen, value, precision, width, flags); - } + const bool negative = SIGN_OF_DOUBLE_NUMBER(number); + // This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it + double abs_number = negative ? -number : number; - const bool negative = SIGN_OF_DOUBLE_NUMBER(value); - if (negative) { - value = -value; - } - - // default precision - if (!(flags & FLAGS_PRECISION)) { - precision = PRINTF_DEFAULT_FLOAT_PRECISION; - } - - int expval; + int exp10; union { uint64_t U; double F; } conv; - conv.F = value; + conv.F = abs_number; - if (value == 0.0) { + // Determine the decimal exponent + if (abs_number == 0.0) { // TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for denormals more generally. - expval = 0; + exp10 = 0; } else { - // determine the decimal exponent // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) 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) + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exp10 so conv.F is now in [1,2) // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 - 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; + exp10 = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^exp10 but we want to be sure it won't overflow + exp2 = (int)(exp10 * 3.321928094887362 + 0.5); + const double z = exp10 * 2.302585092994046 - exp2 * 0.6931471805599453; const double z2 = z * z; conv.U = (uint64_t)(exp2 + 1023) << 52U; // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); // correct for rounding errors - if (value < conv.F) { - expval--; + if (abs_number < conv.F) { + exp10--; conv.F /= 10; } } - // 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; + // the exp10 format is "%+03d" and largest number is "307", so set aside 4-5 characters + unsigned int minwidth = ((exp10 < 100) && (exp10 > -100)) ? 4U : 5U; // in "%g" mode, "precision" is the number of *significant figures* not decimals if (flags & FLAGS_ADAPT_EXP) { // do we want to fall-back to "%f" mode? - if ((value >= 1e-4) && (value < 1e6)) { - if ((int)precision > expval) { - precision = (unsigned)((int)precision - expval - 1); + if ((abs_number >= 1e-4) && (abs_number < 1e6)) { + if ((int)precision > exp10) { + precision = (unsigned)((int)precision - exp10 - 1); } else { precision = 0; } - flags |= FLAGS_PRECISION; // make sure _ftoa respects precision - // no characters in exponent + flags |= FLAGS_PRECISION; // make sure sprint_decimal_number respects precision + // no characters in exp10 minwidth = 0U; - expval = 0; + exp10 = 0; } else { // we use one sigfig for the integer part @@ -604,7 +557,7 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d // will everything fit? unsigned int fwidth = width; if (width > minwidth) { - // we didn't fall-back so subtract the characters required for the exponent + // we didn't fall-back so subtract the characters required for the exp10 fwidth -= minwidth; } else { // not enough characters, so go back to default sizing @@ -615,23 +568,23 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d fwidth = 0U; } - // rescale the float value - if (expval) { - value /= conv.F; + // rescale the float number + if (exp10) { + abs_number /= conv.F; } // output the floating part const size_t start_idx = idx; - idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, precision, fwidth, flags); + idx = sprint_decimal_number(out, buffer, idx, maxlen, negative ? -abs_number : abs_number, precision, fwidth, flags, buf, len); - // output the exponent part + // output the exp10 part if (minwidth) { // output the exponential symbol out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); - // output the exponent value + // output the exp10 number idx = _ntoa(out, buffer, idx, maxlen, - NTOA_ABS(expval), - expval < 0, 10, 0, minwidth-1, + NTOA_ABS(exp10), + exp10 < 0, 10, 0, minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS); // might need to right-pad spaces if (flags & FLAGS_LEFT) { @@ -641,9 +594,53 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d return idx; } #endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + + +static size_t sprint_floating_point(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int precision, unsigned int width, unsigned int flags, bool prefer_exponential) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY integral-part digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD)) { +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + return sprint_exponential_number(out, buffer, idx, maxlen, value, precision, width, flags, buf, len); +#else + // TODO: Perhaps just dump whatever is in buf? + return 0U; +#endif + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + precision = PRINTF_DEFAULT_FLOAT_PRECISION; + } + +#define NUM_DECIMAL_DIGITS_IN_INT64_T 18U + // limit precision so that our integer holding the fractional part does not overflow + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (precision > NUM_DECIMAL_DIGITS_IN_INT64_T)) { + buf[len++] = '0'; // This respects the precision in terms of result length only + precision--; + } + + return prefer_exponential ? + sprint_exponential_number(out, buffer, idx, maxlen, value, precision, width, flags, buf, len) : + sprint_decimal_number(out, buffer, idx, maxlen, value, precision, width, flags, buf, len); +} + #endif // PRINTF_SUPPORT_FLOAT_SPECIFIERS + // internal vsnprintf static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) { @@ -724,211 +721,211 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const format++; } break; - case 'h' : - flags |= FLAGS_SHORT; + case 'h' : + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; format++; - if (*format == 'h') { - flags |= FLAGS_CHAR; - format++; - } - break; + } + break; #if PRINTF_SUPPORT_PTRDIFF_LENGTH_MODIFIER - case 't' : - flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; + case 't' : + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; #endif - case 'j' : - flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - case 'z' : - flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - default : - break; + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z' : + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default : + break; } // evaluate specifier switch (*format) { case 'd' : - case 'i' : - case 'u' : - case 'x' : - case 'X' : - case 'o' : - case 'b' : { - // set the base - numeric_base_t base; - if (*format == 'x' || *format == 'X') { - base = BASE_HEX; - } - else if (*format == 'o') { - base = BASE_OCTAL; - } - else if (*format == 'b') { - base = BASE_BINARY; - } - else { - base = BASE_DECIMAL; - flags &= ~FLAGS_HASH; // no hash for dec format - } - // uppercase - if (*format == 'X') { - flags |= FLAGS_UPPERCASE; - } - - // no plus or space flag for u, x, X, o, b - if ((*format != 'i') && (*format != 'd')) { - flags &= ~(FLAGS_PLUS | FLAGS_SPACE); - } - - // ignore '0' flag when precision is given - if (flags & FLAGS_PRECISION) { - flags &= ~FLAGS_ZEROPAD; - } - - // convert the integer - if ((*format == 'i') || (*format == 'd')) { - // signed - if (flags & FLAGS_LONG_LONG) { + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + // set the base + numeric_base_t base; + if (*format == 'x' || *format == 'X') { + base = BASE_HEX; + } + else if (*format == 'o') { + base = BASE_OCTAL; + } + else if (*format == 'b') { + base = BASE_BINARY; + } + else { + base = BASE_DECIMAL; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { #if PRINTF_SUPPORT_LONG_LONG - const long long value = va_arg(va, long long); - idx = _ntoa(out, buffer, idx, maxlen, NTOA_ABS(value), value < 0, base, precision, width, flags); + const long long value = va_arg(va, long long); + idx = _ntoa(out, buffer, idx, maxlen, NTOA_ABS(value), value < 0, base, precision, width, flags); #endif - } - else if (flags & FLAGS_LONG) { - const long value = va_arg(va, long); - idx = _ntoa(out, buffer, idx, maxlen, NTOA_ABS(value), value < 0, base, precision, width, flags); - } - else { - const int value = (flags & FLAGS_CHAR) ? (signed char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); - idx = _ntoa(out, buffer, idx, maxlen, NTOA_ABS(value), value < 0, base, precision, width, flags); - } - } - else { - // unsigned - if (flags & FLAGS_LONG_LONG) { + } + else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa(out, buffer, idx, maxlen, NTOA_ABS(value), value < 0, base, precision, width, flags); + } + else { + const int value = (flags & FLAGS_CHAR) ? (signed char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); + idx = _ntoa(out, buffer, idx, maxlen, NTOA_ABS(value), value < 0, base, precision, width, flags); + } + } + else { + // unsigned + if (flags & FLAGS_LONG_LONG) { #if PRINTF_SUPPORT_LONG_LONG - idx = _ntoa(out, buffer, idx, maxlen, (NTOA_VALUE_TYPE) va_arg(va, unsigned long long), false, base, precision, width, flags); + idx = _ntoa(out, buffer, idx, maxlen, (NTOA_VALUE_TYPE) va_arg(va, unsigned long long), false, base, precision, width, flags); #endif - } - else if (flags & FLAGS_LONG) { - idx = _ntoa(out, buffer, idx, maxlen, (NTOA_VALUE_TYPE) va_arg(va, unsigned long), false, base, precision, width, flags); - } - else { - const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); - idx = _ntoa(out, buffer, idx, maxlen, (NTOA_VALUE_TYPE) value, false, base, precision, width, flags); - } - } - format++; - break; - } + } + else if (flags & FLAGS_LONG) { + idx = _ntoa(out, buffer, idx, maxlen, (NTOA_VALUE_TYPE) va_arg(va, unsigned long), false, base, precision, width, flags); + } + else { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); + idx = _ntoa(out, buffer, idx, maxlen, (NTOA_VALUE_TYPE) value, false, base, precision, width, flags); + } + } + format++; + break; + } #if PRINTF_SUPPORT_FLOAT_SPECIFIERS -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; + case 'f' : + case 'F' : + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = sprint_floating_point(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags, PRINTF_PREFER_DECIMAL); + format++; + break; #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - case 'e': + 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; + case 'g': + case 'G': + if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = sprint_floating_point(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags, PRINTF_PREFER_EXPONENTIAL); + format++; + break; #endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS #endif // PRINTF_SUPPORT_FLOAT_SPECIFIERS - case 'c' : { - unsigned int l = 1U; - // pre padding - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // char output - out((char)va_arg(va, int), buffer, idx++, maxlen); - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } + case 'c' : { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } - case 's' : { - const char* p = va_arg(va, char*); - if (p == NULL) { - idx = _out_rev(out, buffer, idx, maxlen, ")llun(", 6, width, flags); - } - else { - unsigned int l = _strnlen_s(p, 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); - } - } - // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { - out(*(p++), buffer, idx++, maxlen); - } - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - } - format++; - break; + case 's' : { + const char* p = va_arg(va, char*); + if (p == NULL) { + idx = _out_rev(out, buffer, idx, maxlen, ")llun(", 6, width, flags); + } + else { + unsigned int l = _strnlen_s(p, 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); } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + } + format++; + break; + } - case 'p' : { - width = sizeof(void*) * 2U + 2; // 2 hex chars per byte + the "0x" prefix - flags |= FLAGS_ZEROPAD | FLAGS_POINTER; - uintptr_t value = (uintptr_t)va_arg(va, void*); + case 'p' : { + width = sizeof(void*) * 2U + 2; // 2 hex chars per byte + the "0x" prefix + flags |= FLAGS_ZEROPAD | FLAGS_POINTER; + uintptr_t value = (uintptr_t)va_arg(va, void*); - if (value == (uintptr_t) NULL) { - idx = _out_rev(out, buffer, idx, maxlen, ")lin(", 5, width, flags); - } - else { -#if PRINTF_SUPPORT_LONG_LONG - const bool is_ll = sizeof(uintptr_t) == sizeof(long long); - if (is_ll) { - idx = _ntoa(out, buffer, idx, maxlen, (NTOA_VALUE_TYPE) value, false, BASE_HEX, precision, width, flags); - } - else { -#endif - idx = _ntoa(out, buffer, idx, maxlen, (NTOA_VALUE_TYPE)((uintptr_t)va_arg(va, void*)), false, BASE_HEX, precision, width, flags); -#if PRINTF_SUPPORT_LONG_LONG - } -#endif - } - format++; - break; - } + if (value == (uintptr_t) NULL) { + idx = _out_rev(out, buffer, idx, maxlen, ")lin(", 5, width, flags); + } + else { + #if PRINTF_SUPPORT_LONG_LONG + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) { + idx = _ntoa(out, buffer, idx, maxlen, (NTOA_VALUE_TYPE) value, false, BASE_HEX, precision, width, flags); + } + else { + #endif + idx = _ntoa(out, buffer, idx, maxlen, (NTOA_VALUE_TYPE)((uintptr_t)va_arg(va, void*)), false, BASE_HEX, precision, width, flags); + #if PRINTF_SUPPORT_LONG_LONG + } + #endif + } + format++; + break; + } - case '%' : - out('%', buffer, idx++, maxlen); - format++; - break; + case '%' : + out('%', buffer, idx++, maxlen); + format++; + break; - default : - out(*format, buffer, idx++, maxlen); - format++; - break; + default : + out(*format, buffer, idx++, maxlen); + format++; + break; } }