From 9e247c17d6fcfa92a03b2eef032763665aecd24a Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Fri, 24 Nov 2023 12:49:20 +0800 Subject: [PATCH] Fixes #171: Adds support for long double format specifier and long double input arguments - but _not_ for internal processing of long doubles, they are down-converted to the internal representation type. Tweaked some comment text and removv ed some redundant testcases involving float-promotion relative to Yonggag Luo's original commit. Signed-off-by: Yonggang Luo --- src/printf/printf.c | 55 +++++++++++++++++++----------- test/test_suite_main_testcases.hpp | 31 +++++++++++++++++ 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/src/printf/printf.c b/src/printf/printf.c index e2b74d2..e47bac6 100644 --- a/src/printf/printf.c +++ b/src/printf/printf.c @@ -111,7 +111,7 @@ #endif // Support for the long long integral types (with the ll, z and t length modifiers for specifiers -// %d,%i,%o,%x,%X,%u, and with the %p specifier). Note: 'L' (long double) is not supported. +// %d,%i,%o,%x,%X,%u, and with the %p specifier). #ifndef PRINTF_SUPPORT_LONG_LONG #define PRINTF_SUPPORT_LONG_LONG 1 #endif @@ -144,23 +144,24 @@ #define PRINTF_FLOAT_NOTATION_THRESHOLD ((floating_point_t) PRINTF_EXPAND_THEN_CONCATENATE(1e,PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL)) // internal flag definitions -#define FLAGS_ZEROPAD (1U << 0U) -#define FLAGS_LEFT (1U << 1U) -#define FLAGS_PLUS (1U << 2U) -#define FLAGS_SPACE (1U << 3U) -#define FLAGS_HASH (1U << 4U) -#define FLAGS_UPPERCASE (1U << 5U) -#define FLAGS_CHAR (1U << 6U) -#define FLAGS_SHORT (1U << 7U) -#define FLAGS_INT (1U << 8U) +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_INT (1U << 8U) // Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS -#define FLAGS_LONG (1U << 9U) -#define FLAGS_LONG_LONG (1U << 10U) -#define FLAGS_PRECISION (1U << 11U) -#define FLAGS_ADAPT_EXP (1U << 12U) -#define FLAGS_POINTER (1U << 13U) +#define FLAGS_LONG (1U << 9U) +#define FLAGS_LONG_LONG (1U << 10U) +#define FLAGS_PRECISION (1U << 11U) +#define FLAGS_ADAPT_EXP (1U << 12U) +#define FLAGS_POINTER (1U << 13U) // Note: Similar, but not identical, effect as FLAGS_HASH -#define FLAGS_SIGNED (1U << 14U) +#define FLAGS_SIGNED (1U << 14U) +#define FLAGS_LONG_DOUBLE (1U << 15U) // Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS #ifdef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS @@ -241,6 +242,12 @@ typedef unsigned int printf_size_t; #error "Non-binary-radix floating-point types are unsupported." #endif +/** + * This library supports taking float-point arguments up to and including + * long double's; but - it currently does _not_ support internal + * representation and manipulation of values as long doubles; the options + * are either single-precision `float` or double-precision `double`. + */ #if PRINTF_USE_DOUBLE_INTERNALLY typedef double floating_point_t; #define FP_TYPE_MANT_DIG DBL_MANT_DIG @@ -1169,6 +1176,10 @@ static inline void format_string_loop(output_gadget_t* output, const char* forma ADVANCE_IN_FORMAT_STRING(format); } break; + case 'L' : + flags |= FLAGS_LONG_DOUBLE; + ADVANCE_IN_FORMAT_STRING(format); + break; case 'h' : flags |= FLAGS_SHORT; ADVANCE_IN_FORMAT_STRING(format); @@ -1282,22 +1293,26 @@ static inline void format_string_loop(output_gadget_t* output, const char* forma } #if PRINTF_SUPPORT_DECIMAL_SPECIFIERS case 'f' : - case 'F' : + case 'F' : { + floating_point_t value = (floating_point_t) (flags & FLAGS_LONG_DOUBLE ? va_arg(args, long double) : va_arg(args, double)); if (*format == 'F') flags |= FLAGS_UPPERCASE; - print_floating_point(output, (floating_point_t) va_arg(args, double), precision, width, flags, PRINTF_PREFER_DECIMAL); + print_floating_point(output, value, precision, width, flags, PRINTF_PREFER_DECIMAL); format++; break; + } #endif #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS case 'e': case 'E': case 'g': - case 'G': + case 'G': { + floating_point_t value = (floating_point_t) (flags & FLAGS_LONG_DOUBLE ? va_arg(args, long double) : va_arg(args, double)); if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; - print_floating_point(output, (floating_point_t) va_arg(args, double), precision, width, flags, PRINTF_PREFER_EXPONENTIAL); + print_floating_point(output, value, precision, width, flags, PRINTF_PREFER_EXPONENTIAL); format++; break; + } #endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS case 'c' : { printf_size_t l = 1U; diff --git a/test/test_suite_main_testcases.hpp b/test/test_suite_main_testcases.hpp index 346536d..0615b47 100644 --- a/test/test_suite_main_testcases.hpp +++ b/test/test_suite_main_testcases.hpp @@ -792,6 +792,17 @@ PRINTF_TEST_CASE(floating_point_specifiers_precision_and_flags) PRINTING_CHECK("3.5", ==, sprintf_, buffer, "%.1f", 3.49); PRINTING_CHECK("a0.5 ", ==, sprintf_, buffer, "a%-5.1f", 0.5); PRINTING_CHECK("a0.5 end", ==, sprintf_, buffer, "a%-5.1fend", 0.5); + + /* %f for double */ + PRINTING_CHECK("42.895223123457", ==, sprintf_, buffer, "%.12f", 42.89522312345678); + /* %F for double */ + PRINTING_CHECK("42.895223123457", ==, sprintf_, buffer, "%.12F", 42.89522312345678); + /* %lf for double */ + PRINTING_CHECK("42.895223123457", ==, sprintf_, buffer, "%.12lf", 42.89522312345678); + /* %Lf for long double */ + PRINTING_CHECK("42.895223123457", ==, sprintf_, buffer, "%.12Lf", 42.89522312345678l); + PRINTING_CHECK("42.895223123457", ==, sprintf_, buffer, "%.12Lf", 42.89522312345678L); + #endif #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS PRINTING_CHECK("0.5", ==, sprintf_, buffer, "%.4g", 0.5); @@ -821,6 +832,26 @@ PRINTF_TEST_CASE(floating_point_specifiers_precision_and_flags) PRINTING_CHECK("4.895512e+04", ==, sprintf_, buffer, "%e", 48955.125); PRINTING_CHECK("9.2524e+04", ==, sprintf_, buffer, "%.4e", 92523.5); PRINTING_CHECK("-8.380923438e+04", ==, sprintf_, buffer, "%.9e", -83809.234375); + + /* %g for double */ + PRINTING_CHECK("100.", ==, sprintf_, buffer, "%#.3g", 99.998580932617187500); + /* %G for double */ + PRINTING_CHECK("100.", ==, sprintf_, buffer, "%#.3G", 99.998580932617187500); + /* %lg for double */ + PRINTING_CHECK("100.", ==, sprintf_, buffer, "%#.3lg", 99.998580932617187500); + /* %Lg for long double */ + PRINTING_CHECK("100.", ==, sprintf_, buffer, "%#.3Lg", 99.998580932617187500l); + PRINTING_CHECK("100.", ==, sprintf_, buffer, "%#.3Lg", 99.998580932617187500L); + + /* %e for double */ + PRINTING_CHECK("-8.380923438e+04", ==, sprintf_, buffer, "%.9e", -83809.234375); + /* %E for double */ + PRINTING_CHECK("-8.380923438E+04", ==, sprintf_, buffer, "%.9E", -83809.234375); + /* %le for double */ + PRINTING_CHECK("-8.380923438e+04", ==, sprintf_, buffer, "%.9le", -83809.234375); + /* %Le for long double */ + PRINTING_CHECK("-8.380923438e+04", ==, sprintf_, buffer, "%.9Le", -83809.234375l); + PRINTING_CHECK("-8.380923438e+04", ==, sprintf_, buffer, "%.9Le", -83809.234375L); #endif } #endif // PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS