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

Reply via email to