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 f8139bb7933ce2a214ea225ec99fbe009ebd5953
Author: Marco Casaroli <[email protected]>
AuthorDate: Thu Nov 5 06:51:56 2026 +0100

    libs/libc/stream: Read a long double for the L length modifier.
    
    printf() took %Lf, %Le, %Lg and %La as double conversions: the L modifier
    set the same flag as l, and the conversion read the argument with
    va_arg(ap, double).  A long double has a different size and is passed in
    a different place on arm64, RISC-V and x86_64.  The output was wrong, and
    the arguments after it were also wrong.  On arm64 and RISC-V the low 64
    bits of a binary128 value, taken as a double, can have more than 300
    digits, and a buffer of the correct size for the number then overflows.
    
    Now L sets the flags of ll.  A floating point conversion with these flags
    takes a long double, also in a numbered argument.  An integer conversion
    with L takes a long long, as in GNU libc: it was a long, which is smaller
    on a 32-bit CPU.
    
    The conversion to double means that the output has the precision and the
    range of a double.  If CONFIG_HAVE_LONG_DOUBLE is not defined, nothing
    changes for a floating point conversion.
    
    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 | 51 +++++++++++++++++++++++++++++++++++---
 1 file changed, 48 insertions(+), 3 deletions(-)

diff --git a/libs/libc/stream/lib_libvsprintf.c 
b/libs/libc/stream/lib_libvsprintf.c
index 6a56c94f563..bdd944be9b8 100644
--- a/libs/libc/stream/lib_libvsprintf.c
+++ b/libs/libc/stream/lib_libvsprintf.c
@@ -97,11 +97,21 @@
 #define FL_FLTEXP          0x4000
 #define FL_FLTFIX          0x8000
 
+/* L, as ll, means a long double in a floating point conversion */
+
+#ifdef CONFIG_HAVE_LONG_DOUBLE
+#  define FL_IS_LDOUBLE(f) (((f) & (FL_LONG | FL_REPD_TYPE)) == \
+                            (FL_LONG | FL_REPD_TYPE))
+#else
+#  define FL_IS_LDOUBLE(f) 0
+#endif
+
 #define TYPE_INT           1
 #define TYPE_LONG          2
 #define TYPE_LONG_LONG     3
 #define TYPE_DOUBLE        4
 #define TYPE_CHAR_POINTER  5
+#define TYPE_LONG_DOUBLE   6
 
 /* Support special access to CODE-space strings for Harvard architectures */
 
@@ -538,7 +548,14 @@ set_star:
               continue;
             }
 
-          if (c == 'l' || c == 'L')
+          if (c == 'L')
+            {
+              flags |= FL_LONG | FL_REPD_TYPE;
+              flags &= ~FL_SHORT;
+              continue;
+            }
+
+          if (c == 'l')
             {
               if ((flags & FL_LONG) != 0)
                 {
@@ -607,7 +624,7 @@ set_star:
 
           if (fmt_isfloat(c))
             {
-              type = TYPE_DOUBLE;
+              type = FL_IS_LDOUBLE(flags) ? TYPE_LONG_DOUBLE : TYPE_DOUBLE;
             }
           else if (c == 'i' || c == 'd' || c == 'u' || c == 'p' ||
                    c == 'o' || c == 'x' || c == 'X')
@@ -708,6 +725,15 @@ flt_oper:
               value = arglist->value[argnumber - 1].d;
             }
           else
+#endif
+#ifdef CONFIG_HAVE_LONG_DOUBLE
+          if (FL_IS_LDOUBLE(flags))
+            {
+              /* The output has the precision of a double */
+
+              value = va_arg(ap, long double);
+            }
+          else
 #endif
             {
               value = va_arg(ap, double);
@@ -1015,7 +1041,16 @@ flt_oper:
           if ((flags & FL_ARGNUMBER) == 0)
 #  endif
             {
-              va_arg(ap, double);
+#  ifdef CONFIG_HAVE_LONG_DOUBLE
+              if (FL_IS_LDOUBLE(flags))
+                {
+                  va_arg(ap, long double);
+                }
+              else
+#  endif
+                {
+                  va_arg(ap, double);
+                }
             }
 
           pnt  = "?";
@@ -1598,6 +1633,16 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream,
             break;
 #endif
 
+#ifdef CONFIG_HAVE_LONG_DOUBLE
+          case TYPE_LONG_DOUBLE:
+#  ifdef CONFIG_LIBC_FLOATINGPOINT
+            arglist.value[i].d = va_arg(ap, long double);
+#  else
+            va_arg(ap, long double);
+#  endif
+            break;
+#endif
+
           case TYPE_CHAR_POINTER:
             arglist.value[i].cp = va_arg(ap, FAR char *);
             break;

Reply via email to