This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 47a1893c36a802aa27913393525edbd90d6cb7b9 Author: Marco Casaroli <[email protected]> AuthorDate: Thu Nov 5 06:51:57 2026 +0100 libs/libc/stream: Convert a binary128 long double by hand in printf(). The cast from a binary128 long double to double calls __trunctfdf2() in libgcc. It adds more than 1 KiB to each image with floating point in printf(), also when no format uses %Lf. Convert it with integer code when long double is binary128: on arm64 and RISC-V. It rounds to nearest with ties to even, as the cast does, and it does not set the floating point exception flags. A long double of any other format still uses a cast. With floating point on, this saves 916 bytes of image text on arm64, 1268 on rv32 and 854 on rv64. Part of https://github.com/apache/nuttx/issues/21467 Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- libs/libc/stream/lib_libvsprintf.c | 104 ++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 2 deletions(-) diff --git a/libs/libc/stream/lib_libvsprintf.c b/libs/libc/stream/lib_libvsprintf.c index bdd944be9b8..d2b560572a6 100644 --- a/libs/libc/stream/lib_libvsprintf.c +++ b/libs/libc/stream/lib_libvsprintf.c @@ -123,6 +123,24 @@ #define fmt_ungetc(fmt) ((fmt)--) +/* binary128 converts by hand, so that printf() does not pull in the + * soft-float code of libgcc. Any other long double uses a cast. + */ + +#if defined(CONFIG_LIBC_FLOATINGPOINT) && defined(CONFIG_HAVE_LONG_DOUBLE) && \ + LDBL_MANT_DIG == 113 && DBL_MANT_DIG == 53 +# define HAVE_LDTOD_BY_HAND +# ifdef CONFIG_ENDIAN_BIG +# define LDTOD_HI 0 +# define LDTOD_LO 1 +# else +# define LDTOD_HI 1 +# define LDTOD_LO 0 +# endif +#else +# define vsprintf_ldtod(v) ((double)(v)) +#endif + /* e, f, g and a in either case: 0x20 makes an ASCII letter lower case */ #define fmt_isfloat(c) ((((c) | 0x20) >= 'e' && ((c) | 0x20) <= 'g') || \ @@ -192,6 +210,88 @@ static int vsprintf_repeat(FAR struct lib_outstream_s *stream, int ch, return total_len; } +#ifdef HAVE_LDTOD_BY_HAND +/* Round to nearest, ties to even, as the cast does. All 64-bit shifts have + * a constant count, so a 32-bit CPU needs no libgcc call either. + */ + +static double vsprintf_ldtod(long double value) +{ + union + { + long double ld; + uint64_t w[2]; + } in; + + union + { + double d; + uint64_t u; + } out; + + uint64_t frac; + uint64_t rest; + int exp; + + in.ld = value; + exp = (in.w[LDTOD_HI] >> 48) & 0x7fff; + frac = ((in.w[LDTOD_HI] & 0x0000ffffffffffffull) << 4) | + (in.w[LDTOD_LO] >> 60); + rest = in.w[LDTOD_LO] << 4; + out.u = in.w[LDTOD_HI] & 0x8000000000000000ull; + + if (exp == 0x7fff) + { + /* Infinity, or a NaN: its result stays a NaN and is quiet */ + + out.u |= 0x7ff0000000000000ull | frac; + if ((frac | rest) != 0) + { + out.u |= 0x0008000000000000ull; + } + + return out.d; + } + + exp -= 16383 - 1023; + if (exp >= 0x7ff) + { + out.u |= 0x7ff0000000000000ull; + return out.d; + } + + if (exp <= 0) + { + if (exp < -52) + { + return out.d; + } + + /* A subnormal result: shift the bits into rest and keep a sticky bit */ + + frac |= 0x0010000000000000ull; + do + { + rest = (frac << 63) | (rest >> 1) | (rest & 1); + frac >>= 1; + } + while (exp++ < 0); + + exp = 0; + } + + if ((rest >> 63) != 0 && ((rest << 1) != 0 || (frac & 1) != 0)) + { + frac++; + } + + /* A carry out of frac goes into the exponent, as it must */ + + out.u += ((uint64_t)exp << 52) + frac; + return out.d; +} +#endif + static int vsprintf_putnum(FAR struct lib_outstream_s *stream, FAR char *buf, int len) { @@ -731,7 +831,7 @@ flt_oper: { /* The output has the precision of a double */ - value = va_arg(ap, long double); + value = vsprintf_ldtod(va_arg(ap, long double)); } else #endif @@ -1636,7 +1736,7 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, #ifdef CONFIG_HAVE_LONG_DOUBLE case TYPE_LONG_DOUBLE: # ifdef CONFIG_LIBC_FLOATINGPOINT - arglist.value[i].d = va_arg(ap, long double); + arglist.value[i].d = vsprintf_ldtod(va_arg(ap, long double)); # else va_arg(ap, long double); # endif
