[libcxx-commits] [libcxx] [libc++][print] Adds ostream overloads. (PR #73262)

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Sat Dec 16 04:38:19 PST 2023


https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/73262

>From c0c15902b2cd0dc6d41e22210ae13fea298c6348 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sun, 23 Jul 2023 14:49:12 +0200
Subject: [PATCH 1/2] [libc++][print] Adds ostream overloads.

Finishes implementation of
- P2093R14 Formatted output
- P2539R4  Should the output of std::print to a terminal be
           synchronized with the underlying stream?

Differential Revision: https://reviews.llvm.org/D156609
---
 libcxx/docs/FeatureTestMacroTable.rst         |    2 +-
 libcxx/docs/ImplementationDefinedBehavior.rst |   23 +
 libcxx/docs/ReleaseNotes/18.rst               |    2 +
 libcxx/docs/Status/Cxx23Papers.csv            |    4 +-
 libcxx/docs/Status/FormatIssues.csv           |    4 +-
 libcxx/docs/Status/FormatPaper.csv            |    2 +-
 libcxx/include/__availability                 |    9 +
 libcxx/include/fstream                        |    2 +
 libcxx/include/ostream                        |  148 ++
 libcxx/include/print                          |    6 +-
 libcxx/include/version                        |    2 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |    1 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |    1 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |    1 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |    1 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |    3 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |    1 +
 ...xxabi.v1.stable.noexceptions.nonew.abilist |    1 +
 libcxx/modules/std/ostream.inc                |    5 +-
 libcxx/src/CMakeLists.txt                     |    1 +
 libcxx/src/ostream.cpp                        |   42 +
 libcxx/src/std_stream.h                       |    4 +-
 .../vprint_unicode.pass.cpp                   |  165 ++
 .../test/libcxx/transitive_includes/cxx03.csv |   11 +-
 .../test/libcxx/transitive_includes/cxx11.csv |   11 +-
 .../test/libcxx/transitive_includes/cxx14.csv |   11 +-
 .../test/libcxx/transitive_includes/cxx17.csv |   11 +-
 .../test/libcxx/transitive_includes/cxx20.csv |   11 +-
 .../test/libcxx/transitive_includes/cxx23.csv |   12 +-
 .../test/libcxx/transitive_includes/cxx26.csv |   12 +-
 .../locale-specific_form.pass.cpp             | 2200 +++++++++++++++++
 .../ostream.formatted.print/print.pass.cpp    |  193 ++
 .../ostream.formatted.print/print_tests.h     |   83 +
 .../ostream.formatted.print/println.pass.cpp  |   63 +
 .../vprint_nonunicode.pass.cpp                |  198 ++
 .../vprint_unicode.pass.cpp                   |  197 ++
 .../ostream.version.compile.pass.cpp          |   32 +-
 .../print.version.compile.pass.cpp            |   32 +-
 .../version.version.compile.pass.cpp          |   32 +-
 .../generate_feature_test_macro_components.py |    1 -
 libcxx/utils/libcxx/test/features.py          |   11 +
 41 files changed, 3444 insertions(+), 107 deletions(-)
 create mode 100644 libcxx/src/ostream.cpp
 create mode 100644 libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print_tests.h
 create mode 100644 libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/println.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index d09f65b7cadc0e..ad12b109023154 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -344,7 +344,7 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_out_ptr``                               *unimplemented*
     --------------------------------------------------- -----------------
-    ``__cpp_lib_print``                                 *unimplemented*
+    ``__cpp_lib_print``                                 ``202207L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_const``                       *unimplemented*
     --------------------------------------------------- -----------------
diff --git a/libcxx/docs/ImplementationDefinedBehavior.rst b/libcxx/docs/ImplementationDefinedBehavior.rst
index c1f13d7f1cf160..3000bb7cfa4680 100644
--- a/libcxx/docs/ImplementationDefinedBehavior.rst
+++ b/libcxx/docs/ImplementationDefinedBehavior.rst
@@ -28,6 +28,29 @@ The Standard allows implementations to automatically update the
 This offers a way for users to update the *remote time zone database* and
 give them full control over the process.
 
+
+`[ostream.formatted.print]/3 <http://eel.is/c++draft/ostream.formatted.print#3>`_ A terminal capable of displaying Unicode
+--------------------------------------------------------------------------------------------------------------------------
+
+The Standard specifies that the manner in which a stream is determined to refer
+to a terminal capable of displaying Unicode is implementation-defined. This is
+used for ``std::print`` and similar functions taking an ``ostream&`` argument.
+
+Libc++ determines that a stream is Unicode-capable terminal by:
+
+* First it determines whether the stream's ``rdbuf()`` has an underlying
+  ``FILE*``. This is ``true`` in the following cases:
+
+  * The stream is ``std::cout``, ``std::cerr``, or ``std::clog``.
+
+  * A ``std::basic_filebuf<CharT, Traits>`` derived from ``std::filebuf``.
+
+* The way to determine whether this ``FILE*`` refers to a terminal capable of
+  displaying Unicode is the same as specified for `void vprint_unicode(FILE*
+  stream, string_view fmt, format_args args);
+  <http://eel.is/c++draft/print.fun#7>`_. This function is used for other
+  ``std::print`` overloads that don't take an ``ostream&`` argument.
+
 Listed in the index of implementation-defined behavior
 ======================================================
 
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 9e509db6359c4a..79608c631f1e62 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -41,6 +41,8 @@ What's New in Libc++ 18.0.0?
 
 Implemented Papers
 ------------------
+- P2093R14 Formatted output
+- P2539R4  Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?
 
 - P2497R0 - Testing for success or failure of ``<charconv>`` functions
 - P2697R1 - Interfacing ``bitset`` with ``string_view``
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e03cbff2a08bbf..b1fae06e74b5c7 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -59,7 +59,7 @@
 "`P1467R9 <https://wg21.link/P1467R9>`__","LWG","Extended ``floating-point`` types and standard names","July 2022","",""
 "`P1642R11 <https://wg21.link/P1642R11>`__","LWG","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","July 2022","",""
 "`P1899R3 <https://wg21.link/P1899R3>`__","LWG","``stride_view``","July 2022","","","|ranges|"
-"`P2093R14 <https://wg21.link/P2093R14>`__","LWG","Formatted output","July 2022","|In Progress|"
+"`P2093R14 <https://wg21.link/P2093R14>`__","LWG","Formatted output","July 2022","","|Complete|","18.0"
 "`P2165R4 <https://wg21.link/P2165R4>`__","LWG","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","July 2022","",""
 "`P2278R4 <https://wg21.link/P2278R4>`__","LWG","``cbegin`` should always return a constant iterator","July 2022","","","|ranges|"
 "`P2286R8 <https://wg21.link/P2286R8>`__","LWG","Formatting Ranges","July 2022","|Complete|","16.0","|format| |ranges|"
@@ -99,7 +99,7 @@
 "`P2167R3 <https://wg21.link/P2167R3>`__","LWG", "Improved Proposed Wording for LWG 2114", "November 2022","","",""
 "`P2396R1 <https://wg21.link/P2396R1>`__","LWG", "Concurrency TS 2 fixes ", "November 2022","","","|concurrency TS|"
 "`P2505R5 <https://wg21.link/P2505R5>`__","LWG", "Monadic Functions for ``std::expected``", "November 2022","|Complete|","17.0",""
-"`P2539R4 <https://wg21.link/P2539R4>`__","LWG", "Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?", "November 2022","|In Progress|","","|format|"
+"`P2539R4 <https://wg21.link/P2539R4>`__","LWG", "Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?", "November 2022","|Complete|","18.0","|format|"
 "`P2602R2 <https://wg21.link/P2602R2>`__","LWG", "Poison Pills are Too Toxic", "November 2022","","","|ranges|"
 "`P2708R1 <https://wg21.link/P2708R1>`__","LWG", "No Further Fundamentals TSes", "November 2022","|Nothing to do|","",""
 "","","","","","",""
diff --git a/libcxx/docs/Status/FormatIssues.csv b/libcxx/docs/Status/FormatIssues.csv
index efb3e484f357e7..513988d08036ca 100644
--- a/libcxx/docs/Status/FormatIssues.csv
+++ b/libcxx/docs/Status/FormatIssues.csv
@@ -5,11 +5,11 @@ Number,Name,Standard,Assignee,Status,First released version
 `P1868 <https://wg21.link/P1868>`_,"width: clarifying units of width and precision in std::format (Implements the unicode support.)","C++20",Mark de Wever,|Complete|,14.0
 `P2216 <https://wg21.link/P2216>`_,"std::format improvements","C++20",Mark de Wever,|Complete|,15.0
 `P2418 <https://wg21.link/P2418>`__,"Add support for ``std::generator``-like types to ``std::format``","C++20",Mark de Wever,|Complete|,15.0
-"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","C++23",Mark de Wever,|In Progress|
+"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","C++23",Mark de Wever,|Complete|,"18.0"
 "`P2286R8 <https://wg21.link/P2286R8>`__","Formatting Ranges","C++23","Mark de Wever","|Complete|",16.0
 "`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","C++23","Mark de Wever","|Complete|",15.0
 "`P2585R0 <https://wg21.link/P2585R0>`__","Improving default container formatting","C++23","Mark de Wever","|Complete|",17.0
-"`P2539R4 <https://wg21.link/P2539R4>`__","Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?","C++23","Mark de Wever","|In Progress|"
+"`P2539R4 <https://wg21.link/P2539R4>`__","Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?","C++23","Mark de Wever","|Complete|","18.0"
 "`P2713R1 <https://wg21.link/P2713R1>`__","Escaping improvements in ``std::format``","C++23","Mark de Wever",""
 "`P2675R1 <https://wg21.link/P2675R1>`__","``format``'s width estimation is too approximate and not forward compatible","C++23","Mark de Wever","|Complete|",17.0
 "`P2572R1 <https://wg21.link/P2572R1>`__","``std::format`` fill character allowances","C++23","Mark de Wever","|Complete|",17.0
diff --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv
index 0acde337ccafe1..82da54284c7386 100644
--- a/libcxx/docs/Status/FormatPaper.csv
+++ b/libcxx/docs/Status/FormatPaper.csv
@@ -49,4 +49,4 @@ Section,Description,Dependencies,Assignee,Status,First released version
 "`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output"
 `[print.fun] <https://wg21.link/print.fun>`__,"Output to ``stdout``",,Mark de Wever,|Complete|, 17.0
 `[print.fun] <https://wg21.link/print.fun>`__,"Output to ``FILE*``",,Mark de Wever,|Complete|, 17.0
-`[ostream.formatted.print] <https://wg21.link/ostream.formatted.print>`__,"Output to ``ostream``",,Mark de Wever
+`[ostream.formatted.print] <https://wg21.link/ostream.formatted.print>`__,"Output to ``ostream``",,Mark de Wever,|Complete|, 18.0
diff --git a/libcxx/include/__availability b/libcxx/include/__availability
index b5230b3f56b8d8..e51e324e2b56aa 100644
--- a/libcxx/include/__availability
+++ b/libcxx/include/__availability
@@ -139,6 +139,12 @@
 #  define _LIBCPP_AVAILABILITY_HAS_TZDB 1
 #  define _LIBCPP_AVAILABILITY_TZDB
 
+    // This controls the availability of C++23 <print>, which
+    // has a dependency on the built library (it needs access to
+    // the underlying buffer types of std::cout, std::cerr, and std::clog.
+#  define _LIBCPP_AVAILABILITY_HAS_PRINT 1
+#  define _LIBCPP_AVAILABILITY_PRINT
+
 // Enable additional explicit instantiations of iostreams components. This
 // reduces the number of weak definitions generated in programs that use
 // iostreams by providing a single strong definition in the shared library.
@@ -262,6 +268,9 @@
 #  define _LIBCPP_AVAILABILITY_HAS_TZDB 0
 #  define _LIBCPP_AVAILABILITY_TZDB __attribute__((unavailable))
 
+#  define _LIBCPP_AVAILABILITY_HAS_PRINT 0
+#  define _LIBCPP_AVAILABILITY_PRINT __attribute__((unavailable))
+
 // clang-format off
 #  if (defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 120000)   || \
       (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 150000) || \
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 468ff42dc9bc12..812225d549eab6 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -256,6 +256,8 @@ public:
     inline static const char*
     __make_mdstring(ios_base::openmode __mode) _NOEXCEPT;
 
+    _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI FILE* __file() { return __file_; }
+
   protected:
     // 27.9.1.5 Overridden virtual functions:
     int_type underflow() override;
diff --git a/libcxx/include/ostream b/libcxx/include/ostream
index ef74f5ddc66c70..5c5c30de006857 100644
--- a/libcxx/include/ostream
+++ b/libcxx/include/ostream
@@ -159,13 +159,24 @@ basic_ostream<wchar_t, traits>& operator<<(basic_ostream<wchar_t, traits>&, cons
 template<class traits>
 basic_ostream<wchar_t, traits>& operator<<(basic_ostream<wchar_t, traits>&, const char32_t*) = delete; // since C++20
 
+// [ostream.formatted.print], print functions
+template<class... Args>                                                                                // since C++23
+  void print(ostream& os, format_string<Args...> fmt, Args&&... args);
+template<class... Args>                                                                                // since C++23
+  void println(ostream& os, format_string<Args...> fmt, Args&&... args);
+
+void vprint_unicode(ostream& os, string_view fmt, format_args args);                                   // since C++23
+void vprint_nonunicode(ostream& os, string_view fmt, format_args args);                                // since C++23
 }  // std
 
 */
 
 #include <__assert> // all public C++ headers provide the assertion handler
+#include <__availability>
 #include <__config>
 #include <__exception/operations.h>
+#include <__format/format_args.h>
+#include <__format/format_functions.h>
 #include <__fwd/ostream.h>
 #include <__memory/shared_ptr.h>
 #include <__memory/unique_ptr.h>
@@ -176,10 +187,13 @@ basic_ostream<wchar_t, traits>& operator<<(basic_ostream<wchar_t, traits>&, cons
 #include <__type_traits/void_t.h>
 #include <__utility/declval.h>
 #include <bitset>
+#include <cstdio>
 #include <ios>
 #include <locale>
 #include <new>
+#include <print>
 #include <streambuf>
+#include <string_view>
 #include <version>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -1195,6 +1209,140 @@ extern template class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS basic_ostream<char>;
 extern template class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS basic_ostream<wchar_t>;
 #endif
 
+#if _LIBCPP_STD_VER >= 23
+
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void
+__vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
+  ostream::sentry __s(__os);
+  if (__s) {
+    string __o = vformat(__os.getloc(), __fmt, __args);
+    if (__write_nl)
+      __o += '\n';
+
+    const char* __str = __o.data();
+    size_t __len      = __o.size();
+
+#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+    try {
+#  endif // _LIBCPP_HAS_NO_EXCEPTIONS
+      typedef ostreambuf_iterator<char> _Ip;
+      if (std::__pad_and_output(
+              _Ip(__os),
+              __str,
+              (__os.flags() & ios_base::adjustfield) == ios_base::left ? __str + __len : __str,
+              __str + __len,
+              __os,
+              __os.fill())
+              .failed())
+        __os.setstate(ios_base::badbit | ios_base::failbit);
+
+#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+    } catch (...) {
+      __os.__set_badbit_and_consider_rethrow();
+    }
+#  endif // _LIBCPP_HAS_NO_EXCEPTIONS
+  }
+}
+
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args) {
+  std::__vprint_nonunicode(__os, __fmt, __args, false);
+}
+
+// Returns the FILE* associated with the __os.
+// Returns a nullptr when no FILE* is associated with __os.
+// This function is in the dylib since the type of the buffer associated
+// with std::cout, std::cerr, and std::clog is only known in the dylib.
+//
+// This function implements part of the implementation-defined behavior
+// of [ostream.formatted.print]/3
+//   If the function is vprint_unicode and os is a stream that refers to
+//   a terminal capable of displaying Unicode which is determined in an
+//   implementation-defined manner, writes out to the terminal using the
+//   native Unicode API;
+// Whether the returned FILE* is "a terminal capable of displaying Unicode"
+// is determined in the same way as the print(FILE*, ...) overloads.
+_LIBCPP_AVAILABILITY_PRINT _LIBCPP_EXPORTED_FROM_ABI FILE* __get_ostream_file(ostream& __os);
+
+#  ifndef _LIBCPP_HAS_NO_UNICODE
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_AVAILABILITY_PRINT _LIBCPP_HIDE_FROM_ABI void
+__vprint_unicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
+  FILE* __file = std::__get_ostream_file(__os);
+  if (!__file || !__print::__is_terminal(__file))
+    return std::__vprint_nonunicode(__os, __fmt, __args, __write_nl);
+
+  // [ostream.formatted.print]/3
+  //    If the function is vprint_unicode and os is a stream that refers to a
+  //    terminal capable of displaying Unicode which is determined in an
+  //    implementation-defined manner, writes out to the terminal using the
+  //    native Unicode API; if out contains invalid code units, the behavior is
+  //    undefined and implementations are encouraged to diagnose it. If the
+  //    native Unicode API is used, the function flushes os before writing out.
+  //
+  // This is the path for the native API, start with flushing.
+  __os.flush();
+
+#    ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+  try {
+#    endif // _LIBCPP_HAS_NO_EXCEPTIONS
+    ostream::sentry __s(__os);
+    if (__s) {
+#    ifndef _WIN32
+      __print::__vprint_unicode_posix(__file, __fmt, __args, __write_nl, true);
+#    elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
+    __print::__vprint_unicode_windows(__file, __fmt, __args, __write_nl, true);
+#    else
+#      error "Windows builds with wchar_t disabled are not supported."
+#    endif
+    }
+
+#    ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+  } catch (...) {
+    __os.__set_badbit_and_consider_rethrow();
+  }
+#    endif // _LIBCPP_HAS_NO_EXCEPTIONS
+}
+
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_AVAILABILITY_PRINT _LIBCPP_HIDE_FROM_ABI inline void
+vprint_unicode(ostream& __os, string_view __fmt, format_args __args) {
+  std::__vprint_unicode(__os, __fmt, __args, false);
+}
+#  endif // _LIBCPP_HAS_NO_UNICODE
+
+template <class... _Args>
+_LIBCPP_AVAILABILITY_PRINT _LIBCPP_HIDE_FROM_ABI void
+print(ostream& __os, format_string<_Args...> __fmt, _Args&&... __args) {
+#  ifndef _LIBCPP_HAS_NO_UNICODE
+  if constexpr (__print::__use_unicode)
+    std::__vprint_unicode(__os, __fmt.get(), std::make_format_args(__args...), false);
+  else
+    std::__vprint_nonunicode(__os, __fmt.get(), std::make_format_args(__args...), false);
+#  else  // _LIBCPP_HAS_NO_UNICODE
+  std::__vprint_nonunicode(__os, __fmt.get(), std::make_format_args(__args...), false);
+#  endif // _LIBCPP_HAS_NO_UNICODE
+}
+
+template <class... _Args>
+_LIBCPP_AVAILABILITY_PRINT _LIBCPP_HIDE_FROM_ABI void
+println(ostream& __os, format_string<_Args...> __fmt, _Args&&... __args) {
+#  ifndef _LIBCPP_HAS_NO_UNICODE
+  // Note the wording in the Standard is inefficient. The output of
+  // std::format is a std::string which is then copied. This solution
+  // just appends a newline at the end of the output.
+  if constexpr (__print::__use_unicode)
+    std::__vprint_unicode(__os, __fmt.get(), std::make_format_args(__args...), true);
+  else
+    std::__vprint_nonunicode(__os, __fmt.get(), std::make_format_args(__args...), true);
+#  else  // _LIBCPP_HAS_NO_UNICODE
+  std::__vprint_nonunicode(__os, __fmt.get(), std::make_format_args(__args...), true);
+#  endif // _LIBCPP_HAS_NO_UNICODE
+}
+
+#endif // _LIBCPP_STD_VER >= 23
+
 _LIBCPP_END_NAMESPACE_STD
 
 #if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
diff --git a/libcxx/include/print b/libcxx/include/print
index d119c8bda74976..2174557794e4df 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -198,7 +198,11 @@ inline constexpr bool __use_unicode = true;
 #  endif
 
 _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal(FILE* __stream) {
-#  ifdef _WIN32
+  // The macro _LIBCPP_TESTING_PRINT_IS_TERMINAL is used to change
+  // the behavior in the test. This is not part of the public API.
+#  ifdef _LIBCPP_TESTING_PRINT_IS_TERMINAL
+  return _LIBCPP_TESTING_PRINT_IS_TERMINAL(__stream);
+#  elif defined(_WIN32)
   return std::__is_windows_terminal(__stream);
 #  elif __has_include(<unistd.h>)
   return isatty(fileno(__stream));
diff --git a/libcxx/include/version b/libcxx/include/version
index a91c344c436090..a463ce575912ba 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -454,7 +454,7 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 # undef  __cpp_lib_optional
 # define __cpp_lib_optional                             202110L
 // # define __cpp_lib_out_ptr                              202106L
-// # define __cpp_lib_print                                202207L
+# define __cpp_lib_print                                202207L
 // # define __cpp_lib_ranges_as_const                      202207L
 # define __cpp_lib_ranges_as_rvalue                     202207L
 // # define __cpp_lib_ranges_chunk                         202202L
diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 8daad89f52e6f7..b51af1bb0f9ef2 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1465,6 +1465,7 @@
 {'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 91976f500539da..55987d4c913bbe 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -506,6 +506,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 8a98d42a2a1aa0..d861d890861bf6 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -506,6 +506,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 0c06b5097b83f8..ce0a2e354d6d23 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1465,6 +1465,7 @@
 {'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 16658fdff54932..f55dffc3120905 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1160,6 +1160,7 @@
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
@@ -2025,4 +2026,4 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 49e3579614ee8c..74408ca930c904 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1158,6 +1158,7 @@
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 764e7c37daacb7..618d2968d1a642 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1130,6 +1130,7 @@
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
diff --git a/libcxx/modules/std/ostream.inc b/libcxx/modules/std/ostream.inc
index e8989788b7a544..8fcbfb4bdc1828 100644
--- a/libcxx/modules/std/ostream.inc
+++ b/libcxx/modules/std/ostream.inc
@@ -27,13 +27,14 @@ export namespace std {
 #  endif
   using std::operator<<;
 
-#  if 0
+#  if _LIBCPP_STD_VER >= 23
   // [ostream.formatted.print], print functions
   using std::print;
   using std::println;
 
   using std::vprint_nonunicode;
   using std::vprint_unicode;
-#  endif
+#  endif // _LIBCPP_STD_VER >= 23
+
 #endif // _LIBCPP_HAS_NO_LOCALIZATION
 } // namespace std
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index be0113e6b0a585..329964a001363b 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -89,6 +89,7 @@ if (LIBCXX_ENABLE_LOCALIZATION)
     ios.instantiations.cpp
     iostream.cpp
     locale.cpp
+    ostream.cpp
     regex.cpp
     strstream.cpp
     )
diff --git a/libcxx/src/ostream.cpp b/libcxx/src/ostream.cpp
new file mode 100644
index 00000000000000..b91cb57d607caa
--- /dev/null
+++ b/libcxx/src/ostream.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__availability>
+#include <__config>
+#ifndef _LIBCPP_HAS_NO_FILESYSTEM
+#  include <fstream>
+#endif
+#include <ostream>
+
+#include "std_stream.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+_LIBCPP_AVAILABILITY_PRINT _LIBCPP_EXPORTED_FROM_ABI FILE* __get_ostream_file(ostream& __os) {
+  // dynamic_cast requires RTTI, this only affects users whose vendor builds
+  // the dylib with RTTI disabled. It does not affect users why build with RTTI
+  // disabled with a dylib with RTTI enabled.
+  //
+  // Not returning a FILE* means the stream is not considered a terminal and
+  // the special terminal handling is not done. The terminal handling is mainly
+  // of importance on Windows.
+#ifndef _LIBCPP_HAS_NO_RTTI
+  auto* __rdbuf = __os.rdbuf();
+#  ifndef _LIBCPP_HAS_NO_FILESYSTEM
+  if (auto* __buffer = dynamic_cast<filebuf*>(__rdbuf))
+    return __buffer->__file();
+#  endif
+
+  if (auto* __buffer = dynamic_cast<__stdoutbuf<char>*>(__rdbuf))
+    return __buffer->__file();
+#endif // _LIBCPP_HAS_NO_RTTI
+
+  return nullptr;
+}
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/std_stream.h b/libcxx/src/std_stream.h
index 39a2b70b4f47fd..21388e19a9656a 100644
--- a/libcxx/src/std_stream.h
+++ b/libcxx/src/std_stream.h
@@ -286,7 +286,9 @@ class _LIBCPP_HIDDEN __stdoutbuf
 
     __stdoutbuf(FILE* __fp, state_type* __st);
 
-protected:
+    [[nodiscard]] FILE* __file() { return __file_; }
+
+  protected:
     virtual int_type overflow (int_type __c = traits_type::eof());
     virtual streamsize xsputn(const char_type* __s, streamsize __n);
     virtual int sync();
diff --git a/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp b/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
new file mode 100644
index 00000000000000..6243b250dec393
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
@@ -0,0 +1,165 @@
+//===----------------------------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: no-filesystem
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// XFAIL: availability-fp_to_chars-missing
+// XFAIL: availability-print-missing
+
+// Clang modules do not work with the definiton of _LIBCPP_TESTING_PRINT_IS_TERMINAL
+// XFAIL: clang-modules-build
+// <ostream>
+
+// Tests the implementation of
+//  void __vprint_unicode(ostream& os, string_view fmt,
+//                        format_args args, bool write_nl);
+
+// In the library when the std::cout is redirected to a file it is no
+// longer considered a terminal and the special terminal handling is no
+// longer executed. By testing this function we can "force" emulate a
+// terminal.
+// Note write_nl is tested by the public API.
+
+#include <cstdio>
+bool is_terminal(FILE*);
+#define _LIBCPP_TESTING_PRINT_IS_TERMINAL ::is_terminal
+
+#include "filesystem_test_helper.h"
+#include <cassert>
+#include <fstream>
+#include <iostream>
+#include <ostream>
+#include <sstream>
+
+#include "test_macros.h"
+
+#include <print> // TODO REMOVE
+
+scoped_test_env env;
+std::string filename = env.create_file("output.txt");
+
+int is_terminal_calls        = 0;
+bool should_call_is_terminal = false;
+bool is_terminal_result      = false;
+bool is_terminal(FILE*) {
+  ++is_terminal_calls;
+  assert(should_call_is_terminal);
+  return is_terminal_result;
+}
+
+static void test_is_terminal_not_a_file_stream() {
+  is_terminal_calls       = 0;
+  should_call_is_terminal = false;
+  is_terminal_result      = false;
+  {
+    std::stringstream stream;
+    std::print(stream, "test");
+  }
+  {
+    std::ostringstream stream;
+    std::print(stream, "test");
+  }
+  assert(is_terminal_calls == 0);
+}
+
+static void test_is_terminal_file_stream() {
+  is_terminal_calls       = 0;
+  should_call_is_terminal = true;
+  is_terminal_result      = false;
+  {
+    std::fstream stream(filename);
+    assert(stream.is_open());
+    assert(stream.good());
+    std::print(stream, "test");
+    assert(is_terminal_calls == 1);
+  }
+  {
+    std::ofstream stream(filename);
+    assert(stream.is_open());
+    assert(stream.good());
+    std::print(stream, "test");
+    assert(is_terminal_calls == 2);
+  }
+}
+
+static void test_is_terminal_rdbuf_derived_from_filebuf() {
+  struct my_filebuf : public std::filebuf {};
+
+  is_terminal_calls       = 0;
+  should_call_is_terminal = true;
+  is_terminal_result      = false;
+
+  my_filebuf buf;
+  buf.open(filename, std::ios_base::out);
+  assert(buf.is_open());
+
+  std::ostream stream(&buf);
+  std::print(stream, "test");
+  assert(is_terminal_calls == 1);
+}
+
+static void test_is_terminal_std_cout_cerr_clog() {
+  is_terminal_calls       = 0;
+  should_call_is_terminal = true;
+  is_terminal_result      = false;
+  {
+    std::print(std::cout, "test");
+    assert(is_terminal_calls == 1);
+  }
+  {
+    std::print(std::cerr, "test");
+    assert(is_terminal_calls == 2);
+  }
+  {
+    std::print(std::clog, "test");
+    assert(is_terminal_calls == 3);
+  }
+}
+
+static void test_is_terminal_is_flushed() {
+  struct sync_counter : public std::filebuf {
+    sync_counter() {
+      open(filename, std::ios_base::out);
+      assert(is_open());
+    }
+    int sync_calls = 0;
+
+  protected:
+    int virtual sync() {
+      ++sync_calls;
+      return std::basic_streambuf<char>::sync();
+    }
+  };
+
+  should_call_is_terminal = true;
+  is_terminal_result      = false;
+
+  sync_counter buf;
+  std::ostream stream(&buf);
+
+  // Not a terminal sync is not called.
+  std::print(stream, "");
+  assert(buf.sync_calls == 0);
+
+  // A terminal sync is called.
+  is_terminal_result = true;
+  std::print(stream, "");
+  assert(buf.sync_calls == 1); // only called from the destructor of the sentry
+}
+
+int main(int, char**) {
+  test_is_terminal_not_a_file_stream();
+  test_is_terminal_file_stream();
+  test_is_terminal_rdbuf_derived_from_filebuf();
+  test_is_terminal_std_cout_cerr_clog();
+
+  test_is_terminal_is_flushed();
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 7066de65a91372..3f066342717624 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -576,26 +576,29 @@ optional typeinfo
 optional utility
 optional variant
 optional version
+ostream array
 ostream atomic
 ostream bitset
-ostream cerrno
+ostream cmath
 ostream concepts
 ostream cstddef
 ostream cstdint
+ostream cstdio
 ostream cstdlib
-ostream cstring
-ostream initializer_list
 ostream ios
 ostream iosfwd
 ostream iterator
 ostream limits
 ostream locale
 ostream new
+ostream optional
+ostream print
 ostream stdexcept
 ostream streambuf
 ostream string
+ostream string_view
+ostream tuple
 ostream type_traits
-ostream typeinfo
 ostream version
 print array
 print cerrno
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c4dc664d6ca817..7b443e5a0ec0f9 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -581,26 +581,29 @@ optional typeinfo
 optional utility
 optional variant
 optional version
+ostream array
 ostream atomic
 ostream bitset
-ostream cerrno
+ostream cmath
 ostream concepts
 ostream cstddef
 ostream cstdint
+ostream cstdio
 ostream cstdlib
-ostream cstring
-ostream initializer_list
 ostream ios
 ostream iosfwd
 ostream iterator
 ostream limits
 ostream locale
 ostream new
+ostream optional
+ostream print
 ostream stdexcept
 ostream streambuf
 ostream string
+ostream string_view
+ostream tuple
 ostream type_traits
-ostream typeinfo
 ostream version
 print array
 print cerrno
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 20ee43722d894b..a5b77ec79bb5c3 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -583,26 +583,29 @@ optional typeinfo
 optional utility
 optional variant
 optional version
+ostream array
 ostream atomic
 ostream bitset
-ostream cerrno
+ostream cmath
 ostream concepts
 ostream cstddef
 ostream cstdint
+ostream cstdio
 ostream cstdlib
-ostream cstring
-ostream initializer_list
 ostream ios
 ostream iosfwd
 ostream iterator
 ostream limits
 ostream locale
 ostream new
+ostream optional
+ostream print
 ostream stdexcept
 ostream streambuf
 ostream string
+ostream string_view
+ostream tuple
 ostream type_traits
-ostream typeinfo
 ostream version
 print array
 print cerrno
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 20ee43722d894b..a5b77ec79bb5c3 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -583,26 +583,29 @@ optional typeinfo
 optional utility
 optional variant
 optional version
+ostream array
 ostream atomic
 ostream bitset
-ostream cerrno
+ostream cmath
 ostream concepts
 ostream cstddef
 ostream cstdint
+ostream cstdio
 ostream cstdlib
-ostream cstring
-ostream initializer_list
 ostream ios
 ostream iosfwd
 ostream iterator
 ostream limits
 ostream locale
 ostream new
+ostream optional
+ostream print
 ostream stdexcept
 ostream streambuf
 ostream string
+ostream string_view
+ostream tuple
 ostream type_traits
-ostream typeinfo
 ostream version
 print array
 print cerrno
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index d256370aac4a4a..c8c84867fda459 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -588,26 +588,29 @@ optional typeinfo
 optional utility
 optional variant
 optional version
+ostream array
 ostream atomic
 ostream bitset
-ostream cerrno
+ostream cmath
 ostream concepts
 ostream cstddef
 ostream cstdint
+ostream cstdio
 ostream cstdlib
-ostream cstring
-ostream initializer_list
 ostream ios
 ostream iosfwd
 ostream iterator
 ostream limits
 ostream locale
 ostream new
+ostream optional
+ostream print
 ostream stdexcept
 ostream streambuf
 ostream string
+ostream string_view
+ostream tuple
 ostream type_traits
-ostream typeinfo
 ostream version
 print array
 print cerrno
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 9edc283236480e..a4fa50dc014aba 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -398,19 +398,23 @@ optional initializer_list
 optional limits
 optional new
 optional version
+ostream array
 ostream bitset
-ostream cerrno
+ostream cmath
 ostream cstddef
 ostream cstdint
-ostream cstring
-ostream initializer_list
+ostream cstdio
 ostream ios
 ostream limits
 ostream locale
 ostream new
+ostream optional
+ostream print
+ostream stdexcept
 ostream streambuf
 ostream string
-ostream typeinfo
+ostream string_view
+ostream tuple
 ostream version
 print array
 print cerrno
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 9edc283236480e..a4fa50dc014aba 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -398,19 +398,23 @@ optional initializer_list
 optional limits
 optional new
 optional version
+ostream array
 ostream bitset
-ostream cerrno
+ostream cmath
 ostream cstddef
 ostream cstdint
-ostream cstring
-ostream initializer_list
+ostream cstdio
 ostream ios
 ostream limits
 ostream locale
 ostream new
+ostream optional
+ostream print
+ostream stdexcept
 ostream streambuf
 ostream string
-ostream typeinfo
+ostream string_view
+ostream tuple
 ostream version
 print array
 print cerrno
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
new file mode 100644
index 00000000000000..7e1156acf8335b
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
@@ -0,0 +1,2200 @@
+//===----------------------------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: no-localization
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO PRINT Investigate see https://reviews.llvm.org/D156585
+// UNSUPPORTED: no-filesystem
+
+// XFAIL: availability-fp_to_chars-missing
+// XFAIL: availability-print-missing
+
+// Bionic has minimal locale support, investigate this later.
+// XFAIL: LIBCXX-ANDROID-FIXME
+
+// REQUIRES: locale.en_US.UTF-8
+
+// <format>
+
+// This test checks the locale-specific form for these print functions:
+// template<class... Args>
+//   void print(ostream& os, format_string<Args...> fmt, Args&&... args);
+// template<class... Args>
+//   void println(ostream& os, format_string<Args...> fmt, Args&&... args);
+//
+// void vprint_unicode(ostream& os, string_view fmt, format_args args);
+// void vprint_nonunicode(ostream& os, string_view fmt, format_args args);
+
+#include <cassert>
+#include <ostream>
+
+#include "test_macros.h"
+#include "make_string.h"
+#include "platform_support.h" // locale name macros
+#include "test_format_string.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+
+template <class CharT>
+struct numpunct;
+
+template <>
+struct numpunct<char> : std::numpunct<char> {
+  string_type do_truename() const override { return "yes"; }
+  string_type do_falsename() const override { return "no"; }
+
+  std::string do_grouping() const override { return "\1\2\3\2\1"; };
+  char do_thousands_sep() const override { return '_'; }
+  char do_decimal_point() const override { return '#'; }
+};
+
+template <class... Args>
+static void
+test(std::stringstream& stream, std::string expected, test_format_string<char, Args...> fmt, Args&&... args) {
+  // *** print ***
+  {
+    std::print(stream, fmt, std::forward<Args>(args)...);
+    std::string out = stream.str();
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
+  // *** vprint_unicode ***
+  {
+    stream.str("");
+    ;
+    std::vprint_unicode(stream, fmt.get(), std::make_format_args(args...));
+    std::string out = stream.str();
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
+  // *** vprint_nonunicode ***
+  {
+    stream.str("");
+    ;
+    std::vprint_nonunicode(stream, fmt.get(), std::make_format_args(args...));
+    std::string out = stream.str();
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
+  // *** println ***
+  {
+    expected += '\n'; // Tested last since it changes the expected value.
+    stream.str("");
+    ;
+    std::println(stream, fmt, std::forward<Args>(args)...);
+    std::string out = stream.str();
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
+}
+
+template <class... Args>
+static void test(std::string expected, test_format_string<char, Args...> fmt, Args&&... args) {
+  std::stringstream stream;
+  test(stream, std::move(expected), fmt, std::forward<Args>(args)...);
+}
+
+template <class... Args>
+static void test(std::string expected, std::locale loc, test_format_string<char, Args...> fmt, Args&&... args) {
+  std::stringstream stream;
+  stream.imbue(loc);
+  test(stream, std::move(expected), fmt, std::forward<Args>(args)...);
+}
+
+#ifndef TEST_HAS_NO_UNICODE
+struct numpunct_unicode : std::numpunct<char> {
+  string_type do_truename() const override { return "gültig"; }
+  string_type do_falsename() const override { return "ungültig"; }
+};
+
+#endif // TEST_HAS_NO_UNICODE
+
+static void test_bool() {
+  std::locale loc = std::locale(std::locale(), new numpunct<char>());
+
+  std::locale::global(std::locale(LOCALE_en_US_UTF_8));
+  assert(std::locale().name() == LOCALE_en_US_UTF_8);
+  test("true", "{:L}", true);
+  test("false", "{:L}", false);
+
+  test("yes", loc, "{:L}", true);
+  test("no", loc, "{:L}", false);
+
+  std::locale::global(loc);
+  test("yes", "{:L}", true);
+  test("no", "{:L}", false);
+
+  test("true", std::locale(LOCALE_en_US_UTF_8), "{:L}", true);
+  test("false", std::locale(LOCALE_en_US_UTF_8), "{:L}", false);
+
+#ifndef TEST_HAS_NO_UNICODE
+  std::locale loc_unicode = std::locale(std::locale(), new numpunct_unicode());
+
+  test("gültig", loc_unicode, "{:L}", true);
+  test("ungültig", loc_unicode, "{:L}", false);
+
+  test("gültig   ", loc_unicode, "{:9L}", true);
+  test("gültig!!!", loc_unicode, "{:!<9L}", true);
+  test("_gültig__", loc_unicode, "{:_^9L}", true);
+  test("   gültig", loc_unicode, "{:>9L}", true);
+#endif // TEST_HAS_NO_UNICODE
+}
+
+static void test_integer() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Decimal ***
+  std::locale::global(en_US);
+  test("0", "{:L}", 0);
+  test("1", "{:L}", 1);
+  test("10", "{:L}", 10);
+  test("100", "{:L}", 100);
+  test("1,000", "{:L}", 1'000);
+  test("10,000", "{:L}", 10'000);
+  test("100,000", "{:L}", 100'000);
+  test("1,000,000", "{:L}", 1'000'000);
+  test("10,000,000", "{:L}", 10'000'000);
+  test("100,000,000", "{:L}", 100'000'000);
+  test("1,000,000,000", "{:L}", 1'000'000'000);
+
+  test("-1", "{:L}", -1);
+  test("-10", "{:L}", -10);
+  test("-100", "{:L}", -100);
+  test("-1,000", "{:L}", -1'000);
+  test("-10,000", "{:L}", -10'000);
+  test("-100,000", "{:L}", -100'000);
+  test("-1,000,000", "{:L}", -1'000'000);
+  test("-10,000,000", "{:L}", -10'000'000);
+  test("-100,000,000", "{:L}", -100'000'000);
+  test("-1,000,000,000", "{:L}", -1'000'000'000);
+
+  std::locale::global(loc);
+  test("0", "{:L}", 0);
+  test("1", "{:L}", 1);
+  test("1_0", "{:L}", 10);
+  test("10_0", "{:L}", 100);
+  test("1_00_0", "{:L}", 1'000);
+  test("10_00_0", "{:L}", 10'000);
+  test("100_00_0", "{:L}", 100'000);
+  test("1_000_00_0", "{:L}", 1'000'000);
+  test("10_000_00_0", "{:L}", 10'000'000);
+  test("1_00_000_00_0", "{:L}", 100'000'000);
+  test("1_0_00_000_00_0", "{:L}", 1'000'000'000);
+
+  test("-1", "{:L}", -1);
+  test("-1_0", "{:L}", -10);
+  test("-10_0", "{:L}", -100);
+  test("-1_00_0", "{:L}", -1'000);
+  test("-10_00_0", "{:L}", -10'000);
+  test("-100_00_0", "{:L}", -100'000);
+  test("-1_000_00_0", "{:L}", -1'000'000);
+  test("-10_000_00_0", "{:L}", -10'000'000);
+  test("-1_00_000_00_0", "{:L}", -100'000'000);
+  test("-1_0_00_000_00_0", "{:L}", -1'000'000'000);
+
+  test("0", en_US, "{:L}", 0);
+  test("1", en_US, "{:L}", 1);
+  test("10", en_US, "{:L}", 10);
+  test("100", en_US, "{:L}", 100);
+  test("1,000", en_US, "{:L}", 1'000);
+  test("10,000", en_US, "{:L}", 10'000);
+  test("100,000", en_US, "{:L}", 100'000);
+  test("1,000,000", en_US, "{:L}", 1'000'000);
+  test("10,000,000", en_US, "{:L}", 10'000'000);
+  test("100,000,000", en_US, "{:L}", 100'000'000);
+  test("1,000,000,000", en_US, "{:L}", 1'000'000'000);
+
+  test("-1", en_US, "{:L}", -1);
+  test("-10", en_US, "{:L}", -10);
+  test("-100", en_US, "{:L}", -100);
+  test("-1,000", en_US, "{:L}", -1'000);
+  test("-10,000", en_US, "{:L}", -10'000);
+  test("-100,000", en_US, "{:L}", -100'000);
+  test("-1,000,000", en_US, "{:L}", -1'000'000);
+  test("-10,000,000", en_US, "{:L}", -10'000'000);
+  test("-100,000,000", en_US, "{:L}", -100'000'000);
+  test("-1,000,000,000", en_US, "{:L}", -1'000'000'000);
+
+  std::locale::global(en_US);
+  test("0", loc, "{:L}", 0);
+  test("1", loc, "{:L}", 1);
+  test("1_0", loc, "{:L}", 10);
+  test("10_0", loc, "{:L}", 100);
+  test("1_00_0", loc, "{:L}", 1'000);
+  test("10_00_0", loc, "{:L}", 10'000);
+  test("100_00_0", loc, "{:L}", 100'000);
+  test("1_000_00_0", loc, "{:L}", 1'000'000);
+  test("10_000_00_0", loc, "{:L}", 10'000'000);
+  test("1_00_000_00_0", loc, "{:L}", 100'000'000);
+  test("1_0_00_000_00_0", loc, "{:L}", 1'000'000'000);
+
+  test("-1", loc, "{:L}", -1);
+  test("-1_0", loc, "{:L}", -10);
+  test("-10_0", loc, "{:L}", -100);
+  test("-1_00_0", loc, "{:L}", -1'000);
+  test("-10_00_0", loc, "{:L}", -10'000);
+  test("-100_00_0", loc, "{:L}", -100'000);
+  test("-1_000_00_0", loc, "{:L}", -1'000'000);
+  test("-10_000_00_0", loc, "{:L}", -10'000'000);
+  test("-1_00_000_00_0", loc, "{:L}", -100'000'000);
+  test("-1_0_00_000_00_0", loc, "{:L}", -1'000'000'000);
+
+  // *** Binary ***
+  std::locale::global(en_US);
+  test("0", "{:Lb}", 0b0);
+  test("1", "{:Lb}", 0b1);
+  test("1,000,000,000", "{:Lb}", 0b1'000'000'000);
+
+  test("0b0", "{:#Lb}", 0b0);
+  test("0b1", "{:#Lb}", 0b1);
+  test("0b1,000,000,000", "{:#Lb}", 0b1'000'000'000);
+
+  test("-1", "{:LB}", -0b1);
+  test("-1,000,000,000", "{:LB}", -0b1'000'000'000);
+
+  test("-0B1", "{:#LB}", -0b1);
+  test("-0B1,000,000,000", "{:#LB}", -0b1'000'000'000);
+
+  std::locale::global(loc);
+  test("0", "{:Lb}", 0b0);
+  test("1", "{:Lb}", 0b1);
+  test("1_0_00_000_00_0", "{:Lb}", 0b1'000'000'000);
+
+  test("0b0", "{:#Lb}", 0b0);
+  test("0b1", "{:#Lb}", 0b1);
+  test("0b1_0_00_000_00_0", "{:#Lb}", 0b1'000'000'000);
+
+  test("-1", "{:LB}", -0b1);
+  test("-1_0_00_000_00_0", "{:LB}", -0b1'000'000'000);
+
+  test("-0B1", "{:#LB}", -0b1);
+  test("-0B1_0_00_000_00_0", "{:#LB}", -0b1'000'000'000);
+
+  test("0", en_US, "{:Lb}", 0b0);
+  test("1", en_US, "{:Lb}", 0b1);
+  test("1,000,000,000", en_US, "{:Lb}", 0b1'000'000'000);
+
+  test("0b0", en_US, "{:#Lb}", 0b0);
+  test("0b1", en_US, "{:#Lb}", 0b1);
+  test("0b1,000,000,000", en_US, "{:#Lb}", 0b1'000'000'000);
+
+  test("-1", en_US, "{:LB}", -0b1);
+  test("-1,000,000,000", en_US, "{:LB}", -0b1'000'000'000);
+
+  test("-0B1", en_US, "{:#LB}", -0b1);
+  test("-0B1,000,000,000", en_US, "{:#LB}", -0b1'000'000'000);
+
+  std::locale::global(en_US);
+  test("0", loc, "{:Lb}", 0b0);
+  test("1", loc, "{:Lb}", 0b1);
+  test("1_0_00_000_00_0", loc, "{:Lb}", 0b1'000'000'000);
+
+  test("0b0", loc, "{:#Lb}", 0b0);
+  test("0b1", loc, "{:#Lb}", 0b1);
+  test("0b1_0_00_000_00_0", loc, "{:#Lb}", 0b1'000'000'000);
+
+  test("-1", loc, "{:LB}", -0b1);
+  test("-1_0_00_000_00_0", loc, "{:LB}", -0b1'000'000'000);
+
+  test("-0B1", loc, "{:#LB}", -0b1);
+  test("-0B1_0_00_000_00_0", loc, "{:#LB}", -0b1'000'000'000);
+
+  // *** Octal ***
+  std::locale::global(en_US);
+  test("0", "{:Lo}", 00);
+  test("1", "{:Lo}", 01);
+  test("1,000,000,000", "{:Lo}", 01'000'000'000);
+
+  test("0", "{:#Lo}", 00);
+  test("01", "{:#Lo}", 01);
+  test("01,000,000,000", "{:#Lo}", 01'000'000'000);
+
+  test("-1", "{:Lo}", -01);
+  test("-1,000,000,000", "{:Lo}", -01'000'000'000);
+
+  test("-01", "{:#Lo}", -01);
+  test("-01,000,000,000", "{:#Lo}", -01'000'000'000);
+
+  std::locale::global(loc);
+  test("0", "{:Lo}", 00);
+  test("1", "{:Lo}", 01);
+  test("1_0_00_000_00_0", "{:Lo}", 01'000'000'000);
+
+  test("0", "{:#Lo}", 00);
+  test("01", "{:#Lo}", 01);
+  test("01_0_00_000_00_0", "{:#Lo}", 01'000'000'000);
+
+  test("-1", "{:Lo}", -01);
+  test("-1_0_00_000_00_0", "{:Lo}", -01'000'000'000);
+
+  test("-01", "{:#Lo}", -01);
+  test("-01_0_00_000_00_0", "{:#Lo}", -01'000'000'000);
+
+  test("0", en_US, "{:Lo}", 00);
+  test("1", en_US, "{:Lo}", 01);
+  test("1,000,000,000", en_US, "{:Lo}", 01'000'000'000);
+
+  test("0", en_US, "{:#Lo}", 00);
+  test("01", en_US, "{:#Lo}", 01);
+  test("01,000,000,000", en_US, "{:#Lo}", 01'000'000'000);
+
+  test("-1", en_US, "{:Lo}", -01);
+  test("-1,000,000,000", en_US, "{:Lo}", -01'000'000'000);
+
+  test("-01", en_US, "{:#Lo}", -01);
+  test("-01,000,000,000", en_US, "{:#Lo}", -01'000'000'000);
+
+  std::locale::global(en_US);
+  test("0", loc, "{:Lo}", 00);
+  test("1", loc, "{:Lo}", 01);
+  test("1_0_00_000_00_0", loc, "{:Lo}", 01'000'000'000);
+
+  test("0", loc, "{:#Lo}", 00);
+  test("01", loc, "{:#Lo}", 01);
+  test("01_0_00_000_00_0", loc, "{:#Lo}", 01'000'000'000);
+
+  test("-1", loc, "{:Lo}", -01);
+  test("-1_0_00_000_00_0", loc, "{:Lo}", -01'000'000'000);
+
+  test("-01", loc, "{:#Lo}", -01);
+  test("-01_0_00_000_00_0", loc, "{:#Lo}", -01'000'000'000);
+
+  // *** Hexadecimal ***
+  std::locale::global(en_US);
+  test("0", "{:Lx}", 0x0);
+  test("1", "{:Lx}", 0x1);
+  test("1,000,000,000", "{:Lx}", 0x1'000'000'000);
+
+  test("0x0", "{:#Lx}", 0x0);
+  test("0x1", "{:#Lx}", 0x1);
+  test("0x1,000,000,000", "{:#Lx}", 0x1'000'000'000);
+
+  test("-1", "{:LX}", -0x1);
+  test("-1,000,000,000", "{:LX}", -0x1'000'000'000);
+
+  test("-0X1", "{:#LX}", -0x1);
+  test("-0X1,000,000,000", "{:#LX}", -0x1'000'000'000);
+
+  std::locale::global(loc);
+  test("0", "{:Lx}", 0x0);
+  test("1", "{:Lx}", 0x1);
+  test("1_0_00_000_00_0", "{:Lx}", 0x1'000'000'000);
+
+  test("0x0", "{:#Lx}", 0x0);
+  test("0x1", "{:#Lx}", 0x1);
+  test("0x1_0_00_000_00_0", "{:#Lx}", 0x1'000'000'000);
+
+  test("-1", "{:LX}", -0x1);
+  test("-1_0_00_000_00_0", "{:LX}", -0x1'000'000'000);
+
+  test("-0X1", "{:#LX}", -0x1);
+  test("-0X1_0_00_000_00_0", "{:#LX}", -0x1'000'000'000);
+
+  test("0", en_US, "{:Lx}", 0x0);
+  test("1", en_US, "{:Lx}", 0x1);
+  test("1,000,000,000", en_US, "{:Lx}", 0x1'000'000'000);
+
+  test("0x0", en_US, "{:#Lx}", 0x0);
+  test("0x1", en_US, "{:#Lx}", 0x1);
+  test("0x1,000,000,000", en_US, "{:#Lx}", 0x1'000'000'000);
+
+  test("-1", en_US, "{:LX}", -0x1);
+  test("-1,000,000,000", en_US, "{:LX}", -0x1'000'000'000);
+
+  test("-0X1", en_US, "{:#LX}", -0x1);
+  test("-0X1,000,000,000", en_US, "{:#LX}", -0x1'000'000'000);
+
+  std::locale::global(en_US);
+  test("0", loc, "{:Lx}", 0x0);
+  test("1", loc, "{:Lx}", 0x1);
+  test("1_0_00_000_00_0", loc, "{:Lx}", 0x1'000'000'000);
+
+  test("0x0", loc, "{:#Lx}", 0x0);
+  test("0x1", loc, "{:#Lx}", 0x1);
+  test("0x1_0_00_000_00_0", loc, "{:#Lx}", 0x1'000'000'000);
+
+  test("-1", loc, "{:LX}", -0x1);
+  test("-1_0_00_000_00_0", loc, "{:LX}", -0x1'000'000'000);
+
+  test("-0X1", loc, "{:#LX}", -0x1);
+  test("-0X1_0_00_000_00_0", loc, "{:#LX}", -0x1'000'000'000);
+
+  // *** align-fill & width ***
+  test("4_2", loc, "{:L}", 42);
+
+  test("   4_2", loc, "{:6L}", 42);
+  test("4_2   ", loc, "{:<6L}", 42);
+  test(" 4_2  ", loc, "{:^6L}", 42);
+  test("   4_2", loc, "{:>6L}", 42);
+
+  test("4_2***", loc, "{:*<6L}", 42);
+  test("*4_2**", loc, "{:*^6L}", 42);
+  test("***4_2", loc, "{:*>6L}", 42);
+
+  test("4_a*****", loc, "{:*<8Lx}", 0x4a);
+  test("**4_a***", loc, "{:*^8Lx}", 0x4a);
+  test("*****4_a", loc, "{:*>8Lx}", 0x4a);
+
+  test("0x4_a***", loc, "{:*<#8Lx}", 0x4a);
+  test("*0x4_a**", loc, "{:*^#8Lx}", 0x4a);
+  test("***0x4_a", loc, "{:*>#8Lx}", 0x4a);
+
+  test("4_A*****", loc, "{:*<8LX}", 0x4a);
+  test("**4_A***", loc, "{:*^8LX}", 0x4a);
+  test("*****4_A", loc, "{:*>8LX}", 0x4a);
+
+  test("0X4_A***", loc, "{:*<#8LX}", 0x4a);
+  test("*0X4_A**", loc, "{:*^#8LX}", 0x4a);
+  test("***0X4_A", loc, "{:*>#8LX}", 0x4a);
+
+  // Test whether zero padding is ignored
+  test("4_2   ", loc, "{:<06L}", 42);
+  test(" 4_2  ", loc, "{:^06L}", 42);
+  test("   4_2", loc, "{:>06L}", 42);
+
+  // *** zero-padding & width ***
+  test("   4_2", loc, "{:6L}", 42);
+  test("0004_2", loc, "{:06L}", 42);
+  test("-004_2", loc, "{:06L}", -42);
+
+  test("000004_a", loc, "{:08Lx}", 0x4a);
+  test("0x0004_a", loc, "{:#08Lx}", 0x4a);
+  test("0X0004_A", loc, "{:#08LX}", 0x4a);
+
+  test("-00004_a", loc, "{:08Lx}", -0x4a);
+  test("-0x004_a", loc, "{:#08Lx}", -0x4a);
+  test("-0X004_A", loc, "{:#08LX}", -0x4a);
+}
+
+template <class F>
+static void test_floating_point_hex_lower_case() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Basic ***
+  std::locale::global(en_US);
+  test("1.23456p-3", "{:La}", F(0x1.23456p-3));
+  test("1.23456p-2", "{:La}", F(0x1.23456p-2));
+  test("1.23456p-1", "{:La}", F(0x1.23456p-1));
+  test("1.23456p+0", "{:La}", F(0x1.23456p0));
+  test("1.23456p+1", "{:La}", F(0x1.23456p+1));
+  test("1.23456p+2", "{:La}", F(0x1.23456p+2));
+  test("1.23456p+3", "{:La}", F(0x1.23456p+3));
+  test("1.23456p+20", "{:La}", F(0x1.23456p+20));
+
+  std::locale::global(loc);
+  test("1#23456p-3", "{:La}", F(0x1.23456p-3));
+  test("1#23456p-2", "{:La}", F(0x1.23456p-2));
+  test("1#23456p-1", "{:La}", F(0x1.23456p-1));
+  test("1#23456p+0", "{:La}", F(0x1.23456p0));
+  test("1#23456p+1", "{:La}", F(0x1.23456p+1));
+  test("1#23456p+2", "{:La}", F(0x1.23456p+2));
+  test("1#23456p+3", "{:La}", F(0x1.23456p+3));
+  test("1#23456p+20", "{:La}", F(0x1.23456p+20));
+
+  test("1.23456p-3", en_US, "{:La}", F(0x1.23456p-3));
+  test("1.23456p-2", en_US, "{:La}", F(0x1.23456p-2));
+  test("1.23456p-1", en_US, "{:La}", F(0x1.23456p-1));
+  test("1.23456p+0", en_US, "{:La}", F(0x1.23456p0));
+  test("1.23456p+1", en_US, "{:La}", F(0x1.23456p+1));
+  test("1.23456p+2", en_US, "{:La}", F(0x1.23456p+2));
+  test("1.23456p+3", en_US, "{:La}", F(0x1.23456p+3));
+  test("1.23456p+20", en_US, "{:La}", F(0x1.23456p+20));
+
+  std::locale::global(en_US);
+  test("1#23456p-3", loc, "{:La}", F(0x1.23456p-3));
+  test("1#23456p-2", loc, "{:La}", F(0x1.23456p-2));
+  test("1#23456p-1", loc, "{:La}", F(0x1.23456p-1));
+  test("1#23456p+0", loc, "{:La}", F(0x1.23456p0));
+  test("1#23456p+1", loc, "{:La}", F(0x1.23456p+1));
+  test("1#23456p+2", loc, "{:La}", F(0x1.23456p+2));
+  test("1#23456p+3", loc, "{:La}", F(0x1.23456p+3));
+  test("1#23456p+20", loc, "{:La}", F(0x1.23456p+20));
+
+  // *** Fill, align, zero padding ***
+  std::locale::global(en_US);
+  test("1.23456p+3$$$", "{:$<13La}", F(0x1.23456p3));
+  test("$$$1.23456p+3", "{:$>13La}", F(0x1.23456p3));
+  test("$1.23456p+3$$", "{:$^13La}", F(0x1.23456p3));
+  test("0001.23456p+3", "{:013La}", F(0x1.23456p3));
+  test("-1.23456p+3$$$", "{:$<14La}", F(-0x1.23456p3));
+  test("$$$-1.23456p+3", "{:$>14La}", F(-0x1.23456p3));
+  test("$-1.23456p+3$$", "{:$^14La}", F(-0x1.23456p3));
+  test("-0001.23456p+3", "{:014La}", F(-0x1.23456p3));
+
+  std::locale::global(loc);
+  test("1#23456p+3$$$", "{:$<13La}", F(0x1.23456p3));
+  test("$$$1#23456p+3", "{:$>13La}", F(0x1.23456p3));
+  test("$1#23456p+3$$", "{:$^13La}", F(0x1.23456p3));
+  test("0001#23456p+3", "{:013La}", F(0x1.23456p3));
+  test("-1#23456p+3$$$", "{:$<14La}", F(-0x1.23456p3));
+  test("$$$-1#23456p+3", "{:$>14La}", F(-0x1.23456p3));
+  test("$-1#23456p+3$$", "{:$^14La}", F(-0x1.23456p3));
+  test("-0001#23456p+3", "{:014La}", F(-0x1.23456p3));
+
+  test("1.23456p+3$$$", en_US, "{:$<13La}", F(0x1.23456p3));
+  test("$$$1.23456p+3", en_US, "{:$>13La}", F(0x1.23456p3));
+  test("$1.23456p+3$$", en_US, "{:$^13La}", F(0x1.23456p3));
+  test("0001.23456p+3", en_US, "{:013La}", F(0x1.23456p3));
+  test("-1.23456p+3$$$", en_US, "{:$<14La}", F(-0x1.23456p3));
+  test("$$$-1.23456p+3", en_US, "{:$>14La}", F(-0x1.23456p3));
+  test("$-1.23456p+3$$", en_US, "{:$^14La}", F(-0x1.23456p3));
+  test("-0001.23456p+3", en_US, "{:014La}", F(-0x1.23456p3));
+
+  std::locale::global(en_US);
+  test("1#23456p+3$$$", loc, "{:$<13La}", F(0x1.23456p3));
+  test("$$$1#23456p+3", loc, "{:$>13La}", F(0x1.23456p3));
+  test("$1#23456p+3$$", loc, "{:$^13La}", F(0x1.23456p3));
+  test("0001#23456p+3", loc, "{:013La}", F(0x1.23456p3));
+  test("-1#23456p+3$$$", loc, "{:$<14La}", F(-0x1.23456p3));
+  test("$$$-1#23456p+3", loc, "{:$>14La}", F(-0x1.23456p3));
+  test("$-1#23456p+3$$", loc, "{:$^14La}", F(-0x1.23456p3));
+  test("-0001#23456p+3", loc, "{:014La}", F(-0x1.23456p3));
+}
+
+template <class F>
+static void test_floating_point_hex_upper_case() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Basic ***
+  std::locale::global(en_US);
+  test("1.23456P-3", "{:LA}", F(0x1.23456p-3));
+  test("1.23456P-2", "{:LA}", F(0x1.23456p-2));
+  test("1.23456P-1", "{:LA}", F(0x1.23456p-1));
+  test("1.23456P+0", "{:LA}", F(0x1.23456p0));
+  test("1.23456P+1", "{:LA}", F(0x1.23456p+1));
+  test("1.23456P+2", "{:LA}", F(0x1.23456p+2));
+  test("1.23456P+3", "{:LA}", F(0x1.23456p+3));
+  test("1.23456P+20", "{:LA}", F(0x1.23456p+20));
+
+  std::locale::global(loc);
+  test("1#23456P-3", "{:LA}", F(0x1.23456p-3));
+  test("1#23456P-2", "{:LA}", F(0x1.23456p-2));
+  test("1#23456P-1", "{:LA}", F(0x1.23456p-1));
+  test("1#23456P+0", "{:LA}", F(0x1.23456p0));
+  test("1#23456P+1", "{:LA}", F(0x1.23456p+1));
+  test("1#23456P+2", "{:LA}", F(0x1.23456p+2));
+  test("1#23456P+3", "{:LA}", F(0x1.23456p+3));
+  test("1#23456P+20", "{:LA}", F(0x1.23456p+20));
+
+  test("1.23456P-3", en_US, "{:LA}", F(0x1.23456p-3));
+  test("1.23456P-2", en_US, "{:LA}", F(0x1.23456p-2));
+  test("1.23456P-1", en_US, "{:LA}", F(0x1.23456p-1));
+  test("1.23456P+0", en_US, "{:LA}", F(0x1.23456p0));
+  test("1.23456P+1", en_US, "{:LA}", F(0x1.23456p+1));
+  test("1.23456P+2", en_US, "{:LA}", F(0x1.23456p+2));
+  test("1.23456P+3", en_US, "{:LA}", F(0x1.23456p+3));
+  test("1.23456P+20", en_US, "{:LA}", F(0x1.23456p+20));
+
+  std::locale::global(en_US);
+  test("1#23456P-3", loc, "{:LA}", F(0x1.23456p-3));
+  test("1#23456P-2", loc, "{:LA}", F(0x1.23456p-2));
+  test("1#23456P-1", loc, "{:LA}", F(0x1.23456p-1));
+  test("1#23456P+0", loc, "{:LA}", F(0x1.23456p0));
+  test("1#23456P+1", loc, "{:LA}", F(0x1.23456p+1));
+  test("1#23456P+2", loc, "{:LA}", F(0x1.23456p+2));
+  test("1#23456P+3", loc, "{:LA}", F(0x1.23456p+3));
+  test("1#23456P+20", loc, "{:LA}", F(0x1.23456p+20));
+
+  // *** Fill, align, zero Padding ***
+  std::locale::global(en_US);
+  test("1.23456P+3$$$", "{:$<13LA}", F(0x1.23456p3));
+  test("$$$1.23456P+3", "{:$>13LA}", F(0x1.23456p3));
+  test("$1.23456P+3$$", "{:$^13LA}", F(0x1.23456p3));
+  test("0001.23456P+3", "{:013LA}", F(0x1.23456p3));
+  test("-1.23456P+3$$$", "{:$<14LA}", F(-0x1.23456p3));
+  test("$$$-1.23456P+3", "{:$>14LA}", F(-0x1.23456p3));
+  test("$-1.23456P+3$$", "{:$^14LA}", F(-0x1.23456p3));
+  test("-0001.23456P+3", "{:014LA}", F(-0x1.23456p3));
+
+  std::locale::global(loc);
+  test("1#23456P+3$$$", "{:$<13LA}", F(0x1.23456p3));
+  test("$$$1#23456P+3", "{:$>13LA}", F(0x1.23456p3));
+  test("$1#23456P+3$$", "{:$^13LA}", F(0x1.23456p3));
+  test("0001#23456P+3", "{:013LA}", F(0x1.23456p3));
+  test("-1#23456P+3$$$", "{:$<14LA}", F(-0x1.23456p3));
+  test("$$$-1#23456P+3", "{:$>14LA}", F(-0x1.23456p3));
+  test("$-1#23456P+3$$", "{:$^14LA}", F(-0x1.23456p3));
+  test("-0001#23456P+3", "{:014LA}", F(-0x1.23456p3));
+
+  test("1.23456P+3$$$", en_US, "{:$<13LA}", F(0x1.23456p3));
+  test("$$$1.23456P+3", en_US, "{:$>13LA}", F(0x1.23456p3));
+  test("$1.23456P+3$$", en_US, "{:$^13LA}", F(0x1.23456p3));
+  test("0001.23456P+3", en_US, "{:013LA}", F(0x1.23456p3));
+  test("-1.23456P+3$$$", en_US, "{:$<14LA}", F(-0x1.23456p3));
+  test("$$$-1.23456P+3", en_US, "{:$>14LA}", F(-0x1.23456p3));
+  test("$-1.23456P+3$$", en_US, "{:$^14LA}", F(-0x1.23456p3));
+  test("-0001.23456P+3", en_US, "{:014LA}", F(-0x1.23456p3));
+
+  std::locale::global(en_US);
+  test("1#23456P+3$$$", loc, "{:$<13LA}", F(0x1.23456p3));
+  test("$$$1#23456P+3", loc, "{:$>13LA}", F(0x1.23456p3));
+  test("$1#23456P+3$$", loc, "{:$^13LA}", F(0x1.23456p3));
+  test("0001#23456P+3", loc, "{:013LA}", F(0x1.23456p3));
+  test("-1#23456P+3$$$", loc, "{:$<14LA}", F(-0x1.23456p3));
+  test("$$$-1#23456P+3", loc, "{:$>14LA}", F(-0x1.23456p3));
+  test("$-1#23456P+3$$", loc, "{:$^14LA}", F(-0x1.23456p3));
+  test("-0001#23456P+3", loc, "{:014LA}", F(-0x1.23456p3));
+}
+
+template <class F>
+static void test_floating_point_hex_lower_case_precision() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Basic ***
+  std::locale::global(en_US);
+  test("1.234560p-3", "{:.6La}", F(0x1.23456p-3));
+  test("1.234560p-2", "{:.6La}", F(0x1.23456p-2));
+  test("1.234560p-1", "{:.6La}", F(0x1.23456p-1));
+  test("1.234560p+0", "{:.6La}", F(0x1.23456p0));
+  test("1.234560p+1", "{:.6La}", F(0x1.23456p+1));
+  test("1.234560p+2", "{:.6La}", F(0x1.23456p+2));
+  test("1.234560p+3", "{:.6La}", F(0x1.23456p+3));
+  test("1.234560p+20", "{:.6La}", F(0x1.23456p+20));
+
+  std::locale::global(loc);
+  test("1#234560p-3", "{:.6La}", F(0x1.23456p-3));
+  test("1#234560p-2", "{:.6La}", F(0x1.23456p-2));
+  test("1#234560p-1", "{:.6La}", F(0x1.23456p-1));
+  test("1#234560p+0", "{:.6La}", F(0x1.23456p0));
+  test("1#234560p+1", "{:.6La}", F(0x1.23456p+1));
+  test("1#234560p+2", "{:.6La}", F(0x1.23456p+2));
+  test("1#234560p+3", "{:.6La}", F(0x1.23456p+3));
+  test("1#234560p+20", "{:.6La}", F(0x1.23456p+20));
+
+  test("1.234560p-3", en_US, "{:.6La}", F(0x1.23456p-3));
+  test("1.234560p-2", en_US, "{:.6La}", F(0x1.23456p-2));
+  test("1.234560p-1", en_US, "{:.6La}", F(0x1.23456p-1));
+  test("1.234560p+0", en_US, "{:.6La}", F(0x1.23456p0));
+  test("1.234560p+1", en_US, "{:.6La}", F(0x1.23456p+1));
+  test("1.234560p+2", en_US, "{:.6La}", F(0x1.23456p+2));
+  test("1.234560p+3", en_US, "{:.6La}", F(0x1.23456p+3));
+  test("1.234560p+20", en_US, "{:.6La}", F(0x1.23456p+20));
+
+  std::locale::global(en_US);
+  test("1#234560p-3", loc, "{:.6La}", F(0x1.23456p-3));
+  test("1#234560p-2", loc, "{:.6La}", F(0x1.23456p-2));
+  test("1#234560p-1", loc, "{:.6La}", F(0x1.23456p-1));
+  test("1#234560p+0", loc, "{:.6La}", F(0x1.23456p0));
+  test("1#234560p+1", loc, "{:.6La}", F(0x1.23456p+1));
+  test("1#234560p+2", loc, "{:.6La}", F(0x1.23456p+2));
+  test("1#234560p+3", loc, "{:.6La}", F(0x1.23456p+3));
+  test("1#234560p+20", loc, "{:.6La}", F(0x1.23456p+20));
+
+  // *** Fill, align, zero padding ***
+  std::locale::global(en_US);
+  test("1.234560p+3$$$", "{:$<14.6La}", F(0x1.23456p3));
+  test("$$$1.234560p+3", "{:$>14.6La}", F(0x1.23456p3));
+  test("$1.234560p+3$$", "{:$^14.6La}", F(0x1.23456p3));
+  test("0001.234560p+3", "{:014.6La}", F(0x1.23456p3));
+  test("-1.234560p+3$$$", "{:$<15.6La}", F(-0x1.23456p3));
+  test("$$$-1.234560p+3", "{:$>15.6La}", F(-0x1.23456p3));
+  test("$-1.234560p+3$$", "{:$^15.6La}", F(-0x1.23456p3));
+  test("-0001.234560p+3", "{:015.6La}", F(-0x1.23456p3));
+
+  std::locale::global(loc);
+  test("1#234560p+3$$$", "{:$<14.6La}", F(0x1.23456p3));
+  test("$$$1#234560p+3", "{:$>14.6La}", F(0x1.23456p3));
+  test("$1#234560p+3$$", "{:$^14.6La}", F(0x1.23456p3));
+  test("0001#234560p+3", "{:014.6La}", F(0x1.23456p3));
+  test("-1#234560p+3$$$", "{:$<15.6La}", F(-0x1.23456p3));
+  test("$$$-1#234560p+3", "{:$>15.6La}", F(-0x1.23456p3));
+  test("$-1#234560p+3$$", "{:$^15.6La}", F(-0x1.23456p3));
+  test("-0001#234560p+3", "{:015.6La}", F(-0x1.23456p3));
+
+  test("1.234560p+3$$$", en_US, "{:$<14.6La}", F(0x1.23456p3));
+  test("$$$1.234560p+3", en_US, "{:$>14.6La}", F(0x1.23456p3));
+  test("$1.234560p+3$$", en_US, "{:$^14.6La}", F(0x1.23456p3));
+  test("0001.234560p+3", en_US, "{:014.6La}", F(0x1.23456p3));
+  test("-1.234560p+3$$$", en_US, "{:$<15.6La}", F(-0x1.23456p3));
+  test("$$$-1.234560p+3", en_US, "{:$>15.6La}", F(-0x1.23456p3));
+  test("$-1.234560p+3$$", en_US, "{:$^15.6La}", F(-0x1.23456p3));
+  test("-0001.234560p+3", en_US, "{:015.6La}", F(-0x1.23456p3));
+
+  std::locale::global(en_US);
+  test("1#234560p+3$$$", loc, "{:$<14.6La}", F(0x1.23456p3));
+  test("$$$1#234560p+3", loc, "{:$>14.6La}", F(0x1.23456p3));
+  test("$1#234560p+3$$", loc, "{:$^14.6La}", F(0x1.23456p3));
+  test("0001#234560p+3", loc, "{:014.6La}", F(0x1.23456p3));
+  test("-1#234560p+3$$$", loc, "{:$<15.6La}", F(-0x1.23456p3));
+  test("$$$-1#234560p+3", loc, "{:$>15.6La}", F(-0x1.23456p3));
+  test("$-1#234560p+3$$", loc, "{:$^15.6La}", F(-0x1.23456p3));
+  test("-0001#234560p+3", loc, "{:015.6La}", F(-0x1.23456p3));
+}
+
+template <class F>
+static void test_floating_point_hex_upper_case_precision() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Basic ***
+  std::locale::global(en_US);
+  test("1.234560P-3", "{:.6LA}", F(0x1.23456p-3));
+  test("1.234560P-2", "{:.6LA}", F(0x1.23456p-2));
+  test("1.234560P-1", "{:.6LA}", F(0x1.23456p-1));
+  test("1.234560P+0", "{:.6LA}", F(0x1.23456p0));
+  test("1.234560P+1", "{:.6LA}", F(0x1.23456p+1));
+  test("1.234560P+2", "{:.6LA}", F(0x1.23456p+2));
+  test("1.234560P+3", "{:.6LA}", F(0x1.23456p+3));
+  test("1.234560P+20", "{:.6LA}", F(0x1.23456p+20));
+
+  std::locale::global(loc);
+  test("1#234560P-3", "{:.6LA}", F(0x1.23456p-3));
+  test("1#234560P-2", "{:.6LA}", F(0x1.23456p-2));
+  test("1#234560P-1", "{:.6LA}", F(0x1.23456p-1));
+  test("1#234560P+0", "{:.6LA}", F(0x1.23456p0));
+  test("1#234560P+1", "{:.6LA}", F(0x1.23456p+1));
+  test("1#234560P+2", "{:.6LA}", F(0x1.23456p+2));
+  test("1#234560P+3", "{:.6LA}", F(0x1.23456p+3));
+  test("1#234560P+20", "{:.6LA}", F(0x1.23456p+20));
+
+  test("1.234560P-3", en_US, "{:.6LA}", F(0x1.23456p-3));
+  test("1.234560P-2", en_US, "{:.6LA}", F(0x1.23456p-2));
+  test("1.234560P-1", en_US, "{:.6LA}", F(0x1.23456p-1));
+  test("1.234560P+0", en_US, "{:.6LA}", F(0x1.23456p0));
+  test("1.234560P+1", en_US, "{:.6LA}", F(0x1.23456p+1));
+  test("1.234560P+2", en_US, "{:.6LA}", F(0x1.23456p+2));
+  test("1.234560P+3", en_US, "{:.6LA}", F(0x1.23456p+3));
+  test("1.234560P+20", en_US, "{:.6LA}", F(0x1.23456p+20));
+
+  std::locale::global(en_US);
+  test("1#234560P-3", loc, "{:.6LA}", F(0x1.23456p-3));
+  test("1#234560P-2", loc, "{:.6LA}", F(0x1.23456p-2));
+  test("1#234560P-1", loc, "{:.6LA}", F(0x1.23456p-1));
+  test("1#234560P+0", loc, "{:.6LA}", F(0x1.23456p0));
+  test("1#234560P+1", loc, "{:.6LA}", F(0x1.23456p+1));
+  test("1#234560P+2", loc, "{:.6LA}", F(0x1.23456p+2));
+  test("1#234560P+3", loc, "{:.6LA}", F(0x1.23456p+3));
+  test("1#234560P+20", loc, "{:.6LA}", F(0x1.23456p+20));
+
+  // *** Fill, align, zero Padding ***
+  std::locale::global(en_US);
+  test("1.234560P+3$$$", "{:$<14.6LA}", F(0x1.23456p3));
+  test("$$$1.234560P+3", "{:$>14.6LA}", F(0x1.23456p3));
+  test("$1.234560P+3$$", "{:$^14.6LA}", F(0x1.23456p3));
+  test("0001.234560P+3", "{:014.6LA}", F(0x1.23456p3));
+  test("-1.234560P+3$$$", "{:$<15.6LA}", F(-0x1.23456p3));
+  test("$$$-1.234560P+3", "{:$>15.6LA}", F(-0x1.23456p3));
+  test("$-1.234560P+3$$", "{:$^15.6LA}", F(-0x1.23456p3));
+  test("-0001.234560P+3", "{:015.6LA}", F(-0x1.23456p3));
+
+  std::locale::global(loc);
+  test("1#234560P+3$$$", "{:$<14.6LA}", F(0x1.23456p3));
+  test("$$$1#234560P+3", "{:$>14.6LA}", F(0x1.23456p3));
+  test("$1#234560P+3$$", "{:$^14.6LA}", F(0x1.23456p3));
+  test("0001#234560P+3", "{:014.6LA}", F(0x1.23456p3));
+  test("-1#234560P+3$$$", "{:$<15.6LA}", F(-0x1.23456p3));
+  test("$$$-1#234560P+3", "{:$>15.6LA}", F(-0x1.23456p3));
+  test("$-1#234560P+3$$", "{:$^15.6LA}", F(-0x1.23456p3));
+  test("-0001#234560P+3", "{:015.6LA}", F(-0x1.23456p3));
+
+  test("1.234560P+3$$$", en_US, "{:$<14.6LA}", F(0x1.23456p3));
+  test("$$$1.234560P+3", en_US, "{:$>14.6LA}", F(0x1.23456p3));
+  test("$1.234560P+3$$", en_US, "{:$^14.6LA}", F(0x1.23456p3));
+  test("0001.234560P+3", en_US, "{:014.6LA}", F(0x1.23456p3));
+  test("-1.234560P+3$$$", en_US, "{:$<15.6LA}", F(-0x1.23456p3));
+  test("$$$-1.234560P+3", en_US, "{:$>15.6LA}", F(-0x1.23456p3));
+  test("$-1.234560P+3$$", en_US, "{:$^15.6LA}", F(-0x1.23456p3));
+  test("-0001.234560P+3", en_US, "{:015.6LA}", F(-0x1.23456p3));
+
+  std::locale::global(en_US);
+  test("1#234560P+3$$$", loc, "{:$<14.6LA}", F(0x1.23456p3));
+  test("$$$1#234560P+3", loc, "{:$>14.6LA}", F(0x1.23456p3));
+  test("$1#234560P+3$$", loc, "{:$^14.6LA}", F(0x1.23456p3));
+  test("0001#234560P+3", loc, "{:014.6LA}", F(0x1.23456p3));
+  test("-1#234560P+3$$$", loc, "{:$<15.6LA}", F(-0x1.23456p3));
+  test("$$$-1#234560P+3", loc, "{:$>15.6LA}", F(-0x1.23456p3));
+  test("$-1#234560P+3$$", loc, "{:$^15.6LA}", F(-0x1.23456p3));
+  test("-0001#234560P+3", loc, "{:015.6LA}", F(-0x1.23456p3));
+}
+
+template <class F>
+static void test_floating_point_scientific_lower_case() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Basic ***
+  std::locale::global(en_US);
+  test("1.234567e-03", "{:.6Le}", F(1.234567e-3));
+  test("1.234567e-02", "{:.6Le}", F(1.234567e-2));
+  test("1.234567e-01", "{:.6Le}", F(1.234567e-1));
+  test("1.234567e+00", "{:.6Le}", F(1.234567e0));
+  test("1.234567e+01", "{:.6Le}", F(1.234567e1));
+  test("1.234567e+02", "{:.6Le}", F(1.234567e2));
+  test("1.234567e+03", "{:.6Le}", F(1.234567e3));
+  test("1.234567e+20", "{:.6Le}", F(1.234567e20));
+  test("-1.234567e-03", "{:.6Le}", F(-1.234567e-3));
+  test("-1.234567e-02", "{:.6Le}", F(-1.234567e-2));
+  test("-1.234567e-01", "{:.6Le}", F(-1.234567e-1));
+  test("-1.234567e+00", "{:.6Le}", F(-1.234567e0));
+  test("-1.234567e+01", "{:.6Le}", F(-1.234567e1));
+  test("-1.234567e+02", "{:.6Le}", F(-1.234567e2));
+  test("-1.234567e+03", "{:.6Le}", F(-1.234567e3));
+  test("-1.234567e+20", "{:.6Le}", F(-1.234567e20));
+
+  std::locale::global(loc);
+  test("1#234567e-03", "{:.6Le}", F(1.234567e-3));
+  test("1#234567e-02", "{:.6Le}", F(1.234567e-2));
+  test("1#234567e-01", "{:.6Le}", F(1.234567e-1));
+  test("1#234567e+00", "{:.6Le}", F(1.234567e0));
+  test("1#234567e+01", "{:.6Le}", F(1.234567e1));
+  test("1#234567e+02", "{:.6Le}", F(1.234567e2));
+  test("1#234567e+03", "{:.6Le}", F(1.234567e3));
+  test("1#234567e+20", "{:.6Le}", F(1.234567e20));
+  test("-1#234567e-03", "{:.6Le}", F(-1.234567e-3));
+  test("-1#234567e-02", "{:.6Le}", F(-1.234567e-2));
+  test("-1#234567e-01", "{:.6Le}", F(-1.234567e-1));
+  test("-1#234567e+00", "{:.6Le}", F(-1.234567e0));
+  test("-1#234567e+01", "{:.6Le}", F(-1.234567e1));
+  test("-1#234567e+02", "{:.6Le}", F(-1.234567e2));
+  test("-1#234567e+03", "{:.6Le}", F(-1.234567e3));
+  test("-1#234567e+20", "{:.6Le}", F(-1.234567e20));
+
+  test("1.234567e-03", en_US, "{:.6Le}", F(1.234567e-3));
+  test("1.234567e-02", en_US, "{:.6Le}", F(1.234567e-2));
+  test("1.234567e-01", en_US, "{:.6Le}", F(1.234567e-1));
+  test("1.234567e+00", en_US, "{:.6Le}", F(1.234567e0));
+  test("1.234567e+01", en_US, "{:.6Le}", F(1.234567e1));
+  test("1.234567e+02", en_US, "{:.6Le}", F(1.234567e2));
+  test("1.234567e+03", en_US, "{:.6Le}", F(1.234567e3));
+  test("1.234567e+20", en_US, "{:.6Le}", F(1.234567e20));
+  test("-1.234567e-03", en_US, "{:.6Le}", F(-1.234567e-3));
+  test("-1.234567e-02", en_US, "{:.6Le}", F(-1.234567e-2));
+  test("-1.234567e-01", en_US, "{:.6Le}", F(-1.234567e-1));
+  test("-1.234567e+00", en_US, "{:.6Le}", F(-1.234567e0));
+  test("-1.234567e+01", en_US, "{:.6Le}", F(-1.234567e1));
+  test("-1.234567e+02", en_US, "{:.6Le}", F(-1.234567e2));
+  test("-1.234567e+03", en_US, "{:.6Le}", F(-1.234567e3));
+  test("-1.234567e+20", en_US, "{:.6Le}", F(-1.234567e20));
+
+  std::locale::global(en_US);
+  test("1#234567e-03", loc, "{:.6Le}", F(1.234567e-3));
+  test("1#234567e-02", loc, "{:.6Le}", F(1.234567e-2));
+  test("1#234567e-01", loc, "{:.6Le}", F(1.234567e-1));
+  test("1#234567e+00", loc, "{:.6Le}", F(1.234567e0));
+  test("1#234567e+01", loc, "{:.6Le}", F(1.234567e1));
+  test("1#234567e+02", loc, "{:.6Le}", F(1.234567e2));
+  test("1#234567e+03", loc, "{:.6Le}", F(1.234567e3));
+  test("1#234567e+20", loc, "{:.6Le}", F(1.234567e20));
+  test("-1#234567e-03", loc, "{:.6Le}", F(-1.234567e-3));
+  test("-1#234567e-02", loc, "{:.6Le}", F(-1.234567e-2));
+  test("-1#234567e-01", loc, "{:.6Le}", F(-1.234567e-1));
+  test("-1#234567e+00", loc, "{:.6Le}", F(-1.234567e0));
+  test("-1#234567e+01", loc, "{:.6Le}", F(-1.234567e1));
+  test("-1#234567e+02", loc, "{:.6Le}", F(-1.234567e2));
+  test("-1#234567e+03", loc, "{:.6Le}", F(-1.234567e3));
+  test("-1#234567e+20", loc, "{:.6Le}", F(-1.234567e20));
+
+  // *** Fill, align, zero padding ***
+  std::locale::global(en_US);
+  test("1.234567e+03$$$", "{:$<15.6Le}", F(1.234567e3));
+  test("$$$1.234567e+03", "{:$>15.6Le}", F(1.234567e3));
+  test("$1.234567e+03$$", "{:$^15.6Le}", F(1.234567e3));
+  test("0001.234567e+03", "{:015.6Le}", F(1.234567e3));
+  test("-1.234567e+03$$$", "{:$<16.6Le}", F(-1.234567e3));
+  test("$$$-1.234567e+03", "{:$>16.6Le}", F(-1.234567e3));
+  test("$-1.234567e+03$$", "{:$^16.6Le}", F(-1.234567e3));
+  test("-0001.234567e+03", "{:016.6Le}", F(-1.234567e3));
+
+  std::locale::global(loc);
+  test("1#234567e+03$$$", "{:$<15.6Le}", F(1.234567e3));
+  test("$$$1#234567e+03", "{:$>15.6Le}", F(1.234567e3));
+  test("$1#234567e+03$$", "{:$^15.6Le}", F(1.234567e3));
+  test("0001#234567e+03", "{:015.6Le}", F(1.234567e3));
+  test("-1#234567e+03$$$", "{:$<16.6Le}", F(-1.234567e3));
+  test("$$$-1#234567e+03", "{:$>16.6Le}", F(-1.234567e3));
+  test("$-1#234567e+03$$", "{:$^16.6Le}", F(-1.234567e3));
+  test("-0001#234567e+03", "{:016.6Le}", F(-1.234567e3));
+
+  test("1.234567e+03$$$", en_US, "{:$<15.6Le}", F(1.234567e3));
+  test("$$$1.234567e+03", en_US, "{:$>15.6Le}", F(1.234567e3));
+  test("$1.234567e+03$$", en_US, "{:$^15.6Le}", F(1.234567e3));
+  test("0001.234567e+03", en_US, "{:015.6Le}", F(1.234567e3));
+  test("-1.234567e+03$$$", en_US, "{:$<16.6Le}", F(-1.234567e3));
+  test("$$$-1.234567e+03", en_US, "{:$>16.6Le}", F(-1.234567e3));
+  test("$-1.234567e+03$$", en_US, "{:$^16.6Le}", F(-1.234567e3));
+  test("-0001.234567e+03", en_US, "{:016.6Le}", F(-1.234567e3));
+
+  std::locale::global(en_US);
+  test("1#234567e+03$$$", loc, "{:$<15.6Le}", F(1.234567e3));
+  test("$$$1#234567e+03", loc, "{:$>15.6Le}", F(1.234567e3));
+  test("$1#234567e+03$$", loc, "{:$^15.6Le}", F(1.234567e3));
+  test("0001#234567e+03", loc, "{:015.6Le}", F(1.234567e3));
+  test("-1#234567e+03$$$", loc, "{:$<16.6Le}", F(-1.234567e3));
+  test("$$$-1#234567e+03", loc, "{:$>16.6Le}", F(-1.234567e3));
+  test("$-1#234567e+03$$", loc, "{:$^16.6Le}", F(-1.234567e3));
+  test("-0001#234567e+03", loc, "{:016.6Le}", F(-1.234567e3));
+}
+
+template <class F>
+static void test_floating_point_scientific_upper_case() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Basic ***
+  std::locale::global(en_US);
+  test("1.234567E-03", "{:.6LE}", F(1.234567e-3));
+  test("1.234567E-02", "{:.6LE}", F(1.234567e-2));
+  test("1.234567E-01", "{:.6LE}", F(1.234567e-1));
+  test("1.234567E+00", "{:.6LE}", F(1.234567e0));
+  test("1.234567E+01", "{:.6LE}", F(1.234567e1));
+  test("1.234567E+02", "{:.6LE}", F(1.234567e2));
+  test("1.234567E+03", "{:.6LE}", F(1.234567e3));
+  test("1.234567E+20", "{:.6LE}", F(1.234567e20));
+  test("-1.234567E-03", "{:.6LE}", F(-1.234567e-3));
+  test("-1.234567E-02", "{:.6LE}", F(-1.234567e-2));
+  test("-1.234567E-01", "{:.6LE}", F(-1.234567e-1));
+  test("-1.234567E+00", "{:.6LE}", F(-1.234567e0));
+  test("-1.234567E+01", "{:.6LE}", F(-1.234567e1));
+  test("-1.234567E+02", "{:.6LE}", F(-1.234567e2));
+  test("-1.234567E+03", "{:.6LE}", F(-1.234567e3));
+  test("-1.234567E+20", "{:.6LE}", F(-1.234567e20));
+
+  std::locale::global(loc);
+  test("1#234567E-03", "{:.6LE}", F(1.234567e-3));
+  test("1#234567E-02", "{:.6LE}", F(1.234567e-2));
+  test("1#234567E-01", "{:.6LE}", F(1.234567e-1));
+  test("1#234567E+00", "{:.6LE}", F(1.234567e0));
+  test("1#234567E+01", "{:.6LE}", F(1.234567e1));
+  test("1#234567E+02", "{:.6LE}", F(1.234567e2));
+  test("1#234567E+03", "{:.6LE}", F(1.234567e3));
+  test("1#234567E+20", "{:.6LE}", F(1.234567e20));
+  test("-1#234567E-03", "{:.6LE}", F(-1.234567e-3));
+  test("-1#234567E-02", "{:.6LE}", F(-1.234567e-2));
+  test("-1#234567E-01", "{:.6LE}", F(-1.234567e-1));
+  test("-1#234567E+00", "{:.6LE}", F(-1.234567e0));
+  test("-1#234567E+01", "{:.6LE}", F(-1.234567e1));
+  test("-1#234567E+02", "{:.6LE}", F(-1.234567e2));
+  test("-1#234567E+03", "{:.6LE}", F(-1.234567e3));
+  test("-1#234567E+20", "{:.6LE}", F(-1.234567e20));
+
+  test("1.234567E-03", en_US, "{:.6LE}", F(1.234567e-3));
+  test("1.234567E-02", en_US, "{:.6LE}", F(1.234567e-2));
+  test("1.234567E-01", en_US, "{:.6LE}", F(1.234567e-1));
+  test("1.234567E+00", en_US, "{:.6LE}", F(1.234567e0));
+  test("1.234567E+01", en_US, "{:.6LE}", F(1.234567e1));
+  test("1.234567E+02", en_US, "{:.6LE}", F(1.234567e2));
+  test("1.234567E+03", en_US, "{:.6LE}", F(1.234567e3));
+  test("1.234567E+20", en_US, "{:.6LE}", F(1.234567e20));
+  test("-1.234567E-03", en_US, "{:.6LE}", F(-1.234567e-3));
+  test("-1.234567E-02", en_US, "{:.6LE}", F(-1.234567e-2));
+  test("-1.234567E-01", en_US, "{:.6LE}", F(-1.234567e-1));
+  test("-1.234567E+00", en_US, "{:.6LE}", F(-1.234567e0));
+  test("-1.234567E+01", en_US, "{:.6LE}", F(-1.234567e1));
+  test("-1.234567E+02", en_US, "{:.6LE}", F(-1.234567e2));
+  test("-1.234567E+03", en_US, "{:.6LE}", F(-1.234567e3));
+  test("-1.234567E+20", en_US, "{:.6LE}", F(-1.234567e20));
+
+  std::locale::global(en_US);
+  test("1#234567E-03", loc, "{:.6LE}", F(1.234567e-3));
+  test("1#234567E-02", loc, "{:.6LE}", F(1.234567e-2));
+  test("1#234567E-01", loc, "{:.6LE}", F(1.234567e-1));
+  test("1#234567E+00", loc, "{:.6LE}", F(1.234567e0));
+  test("1#234567E+01", loc, "{:.6LE}", F(1.234567e1));
+  test("1#234567E+02", loc, "{:.6LE}", F(1.234567e2));
+  test("1#234567E+03", loc, "{:.6LE}", F(1.234567e3));
+  test("1#234567E+20", loc, "{:.6LE}", F(1.234567e20));
+  test("-1#234567E-03", loc, "{:.6LE}", F(-1.234567e-3));
+  test("-1#234567E-02", loc, "{:.6LE}", F(-1.234567e-2));
+  test("-1#234567E-01", loc, "{:.6LE}", F(-1.234567e-1));
+  test("-1#234567E+00", loc, "{:.6LE}", F(-1.234567e0));
+  test("-1#234567E+01", loc, "{:.6LE}", F(-1.234567e1));
+  test("-1#234567E+02", loc, "{:.6LE}", F(-1.234567e2));
+  test("-1#234567E+03", loc, "{:.6LE}", F(-1.234567e3));
+  test("-1#234567E+20", loc, "{:.6LE}", F(-1.234567e20));
+
+  // *** Fill, align, zero padding ***
+  std::locale::global(en_US);
+  test("1.234567E+03$$$", "{:$<15.6LE}", F(1.234567e3));
+  test("$$$1.234567E+03", "{:$>15.6LE}", F(1.234567e3));
+  test("$1.234567E+03$$", "{:$^15.6LE}", F(1.234567e3));
+  test("0001.234567E+03", "{:015.6LE}", F(1.234567e3));
+  test("-1.234567E+03$$$", "{:$<16.6LE}", F(-1.234567e3));
+  test("$$$-1.234567E+03", "{:$>16.6LE}", F(-1.234567e3));
+  test("$-1.234567E+03$$", "{:$^16.6LE}", F(-1.234567e3));
+  test("-0001.234567E+03", "{:016.6LE}", F(-1.234567e3));
+
+  std::locale::global(loc);
+  test("1#234567E+03$$$", "{:$<15.6LE}", F(1.234567e3));
+  test("$$$1#234567E+03", "{:$>15.6LE}", F(1.234567e3));
+  test("$1#234567E+03$$", "{:$^15.6LE}", F(1.234567e3));
+  test("0001#234567E+03", "{:015.6LE}", F(1.234567e3));
+  test("-1#234567E+03$$$", "{:$<16.6LE}", F(-1.234567e3));
+  test("$$$-1#234567E+03", "{:$>16.6LE}", F(-1.234567e3));
+  test("$-1#234567E+03$$", "{:$^16.6LE}", F(-1.234567e3));
+  test("-0001#234567E+03", "{:016.6LE}", F(-1.234567e3));
+
+  test("1.234567E+03$$$", en_US, "{:$<15.6LE}", F(1.234567e3));
+  test("$$$1.234567E+03", en_US, "{:$>15.6LE}", F(1.234567e3));
+  test("$1.234567E+03$$", en_US, "{:$^15.6LE}", F(1.234567e3));
+  test("0001.234567E+03", en_US, "{:015.6LE}", F(1.234567e3));
+  test("-1.234567E+03$$$", en_US, "{:$<16.6LE}", F(-1.234567e3));
+  test("$$$-1.234567E+03", en_US, "{:$>16.6LE}", F(-1.234567e3));
+  test("$-1.234567E+03$$", en_US, "{:$^16.6LE}", F(-1.234567e3));
+  test("-0001.234567E+03", en_US, "{:016.6LE}", F(-1.234567e3));
+
+  std::locale::global(en_US);
+  test("1#234567E+03$$$", loc, "{:$<15.6LE}", F(1.234567e3));
+  test("$$$1#234567E+03", loc, "{:$>15.6LE}", F(1.234567e3));
+  test("$1#234567E+03$$", loc, "{:$^15.6LE}", F(1.234567e3));
+  test("0001#234567E+03", loc, "{:015.6LE}", F(1.234567e3));
+  test("-1#234567E+03$$$", loc, "{:$<16.6LE}", F(-1.234567e3));
+  test("$$$-1#234567E+03", loc, "{:$>16.6LE}", F(-1.234567e3));
+  test("$-1#234567E+03$$", loc, "{:$^16.6LE}", F(-1.234567e3));
+  test("-0001#234567E+03", loc, "{:016.6LE}", F(-1.234567e3));
+}
+
+template <class F>
+static void test_floating_point_fixed_lower_case() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Basic ***
+  std::locale::global(en_US);
+  test("0.000001", "{:.6Lf}", F(1.234567e-6));
+  test("0.000012", "{:.6Lf}", F(1.234567e-5));
+  test("0.000123", "{:.6Lf}", F(1.234567e-4));
+  test("0.001235", "{:.6Lf}", F(1.234567e-3));
+  test("0.012346", "{:.6Lf}", F(1.234567e-2));
+  test("0.123457", "{:.6Lf}", F(1.234567e-1));
+  test("1.234567", "{:.6Lf}", F(1.234567e0));
+  test("12.345670", "{:.6Lf}", F(1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("123.456700", "{:.6Lf}", F(1.234567e2));
+    test("1,234.567000", "{:.6Lf}", F(1.234567e3));
+    test("12,345.670000", "{:.6Lf}", F(1.234567e4));
+    test("123,456.700000", "{:.6Lf}", F(1.234567e5));
+    test("1,234,567.000000", "{:.6Lf}", F(1.234567e6));
+    test("12,345,670.000000", "{:.6Lf}", F(1.234567e7));
+    test("123,456,700,000,000,000,000.000000", "{:.6Lf}", F(1.234567e20));
+  }
+  test("-0.000001", "{:.6Lf}", F(-1.234567e-6));
+  test("-0.000012", "{:.6Lf}", F(-1.234567e-5));
+  test("-0.000123", "{:.6Lf}", F(-1.234567e-4));
+  test("-0.001235", "{:.6Lf}", F(-1.234567e-3));
+  test("-0.012346", "{:.6Lf}", F(-1.234567e-2));
+  test("-0.123457", "{:.6Lf}", F(-1.234567e-1));
+  test("-1.234567", "{:.6Lf}", F(-1.234567e0));
+  test("-12.345670", "{:.6Lf}", F(-1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("-123.456700", "{:.6Lf}", F(-1.234567e2));
+    test("-1,234.567000", "{:.6Lf}", F(-1.234567e3));
+    test("-12,345.670000", "{:.6Lf}", F(-1.234567e4));
+    test("-123,456.700000", "{:.6Lf}", F(-1.234567e5));
+    test("-1,234,567.000000", "{:.6Lf}", F(-1.234567e6));
+    test("-12,345,670.000000", "{:.6Lf}", F(-1.234567e7));
+    test("-123,456,700,000,000,000,000.000000", "{:.6Lf}", F(-1.234567e20));
+  }
+
+  std::locale::global(loc);
+  test("0#000001", "{:.6Lf}", F(1.234567e-6));
+  test("0#000012", "{:.6Lf}", F(1.234567e-5));
+  test("0#000123", "{:.6Lf}", F(1.234567e-4));
+  test("0#001235", "{:.6Lf}", F(1.234567e-3));
+  test("0#012346", "{:.6Lf}", F(1.234567e-2));
+  test("0#123457", "{:.6Lf}", F(1.234567e-1));
+  test("1#234567", "{:.6Lf}", F(1.234567e0));
+  test("1_2#345670", "{:.6Lf}", F(1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("12_3#456700", "{:.6Lf}", F(1.234567e2));
+    test("1_23_4#567000", "{:.6Lf}", F(1.234567e3));
+    test("12_34_5#670000", "{:.6Lf}", F(1.234567e4));
+    test("123_45_6#700000", "{:.6Lf}", F(1.234567e5));
+    test("1_234_56_7#000000", "{:.6Lf}", F(1.234567e6));
+    test("12_345_67_0#000000", "{:.6Lf}", F(1.234567e7));
+    test("1_2_3_4_5_6_7_0_0_0_0_0_0_00_000_00_0#000000", "{:.6Lf}", F(1.234567e20));
+  }
+  test("-0#000001", "{:.6Lf}", F(-1.234567e-6));
+  test("-0#000012", "{:.6Lf}", F(-1.234567e-5));
+  test("-0#000123", "{:.6Lf}", F(-1.234567e-4));
+  test("-0#001235", "{:.6Lf}", F(-1.234567e-3));
+  test("-0#012346", "{:.6Lf}", F(-1.234567e-2));
+  test("-0#123457", "{:.6Lf}", F(-1.234567e-1));
+  test("-1#234567", "{:.6Lf}", F(-1.234567e0));
+  test("-1_2#345670", "{:.6Lf}", F(-1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("-12_3#456700", "{:.6Lf}", F(-1.234567e2));
+    test("-1_23_4#567000", "{:.6Lf}", F(-1.234567e3));
+    test("-12_34_5#670000", "{:.6Lf}", F(-1.234567e4));
+    test("-123_45_6#700000", "{:.6Lf}", F(-1.234567e5));
+    test("-1_234_56_7#000000", "{:.6Lf}", F(-1.234567e6));
+    test("-12_345_67_0#000000", "{:.6Lf}", F(-1.234567e7));
+    test("-1_2_3_4_5_6_7_0_0_0_0_0_0_00_000_00_0#000000", "{:.6Lf}", F(-1.234567e20));
+  }
+
+  test("0.000001", en_US, "{:.6Lf}", F(1.234567e-6));
+  test("0.000012", en_US, "{:.6Lf}", F(1.234567e-5));
+  test("0.000123", en_US, "{:.6Lf}", F(1.234567e-4));
+  test("0.001235", en_US, "{:.6Lf}", F(1.234567e-3));
+  test("0.012346", en_US, "{:.6Lf}", F(1.234567e-2));
+  test("0.123457", en_US, "{:.6Lf}", F(1.234567e-1));
+  test("1.234567", en_US, "{:.6Lf}", F(1.234567e0));
+  test("12.345670", en_US, "{:.6Lf}", F(1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("123.456700", en_US, "{:.6Lf}", F(1.234567e2));
+    test("1,234.567000", en_US, "{:.6Lf}", F(1.234567e3));
+    test("12,345.670000", en_US, "{:.6Lf}", F(1.234567e4));
+    test("123,456.700000", en_US, "{:.6Lf}", F(1.234567e5));
+    test("1,234,567.000000", en_US, "{:.6Lf}", F(1.234567e6));
+    test("12,345,670.000000", en_US, "{:.6Lf}", F(1.234567e7));
+    test("123,456,700,000,000,000,000.000000", en_US, "{:.6Lf}", F(1.234567e20));
+  }
+  test("-0.000001", en_US, "{:.6Lf}", F(-1.234567e-6));
+  test("-0.000012", en_US, "{:.6Lf}", F(-1.234567e-5));
+  test("-0.000123", en_US, "{:.6Lf}", F(-1.234567e-4));
+  test("-0.001235", en_US, "{:.6Lf}", F(-1.234567e-3));
+  test("-0.012346", en_US, "{:.6Lf}", F(-1.234567e-2));
+  test("-0.123457", en_US, "{:.6Lf}", F(-1.234567e-1));
+  test("-1.234567", en_US, "{:.6Lf}", F(-1.234567e0));
+  test("-12.345670", en_US, "{:.6Lf}", F(-1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("-123.456700", en_US, "{:.6Lf}", F(-1.234567e2));
+    test("-1,234.567000", en_US, "{:.6Lf}", F(-1.234567e3));
+    test("-12,345.670000", en_US, "{:.6Lf}", F(-1.234567e4));
+    test("-123,456.700000", en_US, "{:.6Lf}", F(-1.234567e5));
+    test("-1,234,567.000000", en_US, "{:.6Lf}", F(-1.234567e6));
+    test("-12,345,670.000000", en_US, "{:.6Lf}", F(-1.234567e7));
+    test("-123,456,700,000,000,000,000.000000", en_US, "{:.6Lf}", F(-1.234567e20));
+  }
+
+  std::locale::global(en_US);
+  test("0#000001", loc, "{:.6Lf}", F(1.234567e-6));
+  test("0#000012", loc, "{:.6Lf}", F(1.234567e-5));
+  test("0#000123", loc, "{:.6Lf}", F(1.234567e-4));
+  test("0#001235", loc, "{:.6Lf}", F(1.234567e-3));
+  test("0#012346", loc, "{:.6Lf}", F(1.234567e-2));
+  test("0#123457", loc, "{:.6Lf}", F(1.234567e-1));
+  test("1#234567", loc, "{:.6Lf}", F(1.234567e0));
+  test("1_2#345670", loc, "{:.6Lf}", F(1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("12_3#456700", loc, "{:.6Lf}", F(1.234567e2));
+    test("1_23_4#567000", loc, "{:.6Lf}", F(1.234567e3));
+    test("12_34_5#670000", loc, "{:.6Lf}", F(1.234567e4));
+    test("123_45_6#700000", loc, "{:.6Lf}", F(1.234567e5));
+    test("1_234_56_7#000000", loc, "{:.6Lf}", F(1.234567e6));
+    test("12_345_67_0#000000", loc, "{:.6Lf}", F(1.234567e7));
+    test("1_2_3_4_5_6_7_0_0_0_0_0_0_00_000_00_0#000000", loc, "{:.6Lf}", F(1.234567e20));
+  }
+  test("-0#000001", loc, "{:.6Lf}", F(-1.234567e-6));
+  test("-0#000012", loc, "{:.6Lf}", F(-1.234567e-5));
+  test("-0#000123", loc, "{:.6Lf}", F(-1.234567e-4));
+  test("-0#001235", loc, "{:.6Lf}", F(-1.234567e-3));
+  test("-0#012346", loc, "{:.6Lf}", F(-1.234567e-2));
+  test("-0#123457", loc, "{:.6Lf}", F(-1.234567e-1));
+  test("-1#234567", loc, "{:.6Lf}", F(-1.234567e0));
+  test("-1_2#345670", loc, "{:.6Lf}", F(-1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("-12_3#456700", loc, "{:.6Lf}", F(-1.234567e2));
+    test("-1_23_4#567000", loc, "{:.6Lf}", F(-1.234567e3));
+    test("-12_34_5#670000", loc, "{:.6Lf}", F(-1.234567e4));
+    test("-123_45_6#700000", loc, "{:.6Lf}", F(-1.234567e5));
+    test("-1_234_56_7#000000", loc, "{:.6Lf}", F(-1.234567e6));
+    test("-12_345_67_0#000000", loc, "{:.6Lf}", F(-1.234567e7));
+    test("-1_2_3_4_5_6_7_0_0_0_0_0_0_00_000_00_0#000000", loc, "{:.6Lf}", F(-1.234567e20));
+  }
+
+  // *** Fill, align, zero padding ***
+  if constexpr (sizeof(F) > sizeof(float)) {
+    std::locale::global(en_US);
+    test("1,234.567000$$$", "{:$<15.6Lf}", F(1.234567e3));
+    test("$$$1,234.567000", "{:$>15.6Lf}", F(1.234567e3));
+    test("$1,234.567000$$", "{:$^15.6Lf}", F(1.234567e3));
+    test("0001,234.567000", "{:015.6Lf}", F(1.234567e3));
+    test("-1,234.567000$$$", "{:$<16.6Lf}", F(-1.234567e3));
+    test("$$$-1,234.567000", "{:$>16.6Lf}", F(-1.234567e3));
+    test("$-1,234.567000$$", "{:$^16.6Lf}", F(-1.234567e3));
+    test("-0001,234.567000", "{:016.6Lf}", F(-1.234567e3));
+
+    std::locale::global(loc);
+    test("1_23_4#567000$$$", "{:$<16.6Lf}", F(1.234567e3));
+    test("$$$1_23_4#567000", "{:$>16.6Lf}", F(1.234567e3));
+    test("$1_23_4#567000$$", "{:$^16.6Lf}", F(1.234567e3));
+    test("0001_23_4#567000", "{:016.6Lf}", F(1.234567e3));
+    test("-1_23_4#567000$$$", "{:$<17.6Lf}", F(-1.234567e3));
+    test("$$$-1_23_4#567000", "{:$>17.6Lf}", F(-1.234567e3));
+    test("$-1_23_4#567000$$", "{:$^17.6Lf}", F(-1.234567e3));
+    test("-0001_23_4#567000", "{:017.6Lf}", F(-1.234567e3));
+
+    test("1,234.567000$$$", en_US, "{:$<15.6Lf}", F(1.234567e3));
+    test("$$$1,234.567000", en_US, "{:$>15.6Lf}", F(1.234567e3));
+    test("$1,234.567000$$", en_US, "{:$^15.6Lf}", F(1.234567e3));
+    test("0001,234.567000", en_US, "{:015.6Lf}", F(1.234567e3));
+    test("-1,234.567000$$$", en_US, "{:$<16.6Lf}", F(-1.234567e3));
+    test("$$$-1,234.567000", en_US, "{:$>16.6Lf}", F(-1.234567e3));
+    test("$-1,234.567000$$", en_US, "{:$^16.6Lf}", F(-1.234567e3));
+    test("-0001,234.567000", en_US, "{:016.6Lf}", F(-1.234567e3));
+
+    std::locale::global(en_US);
+    test("1_23_4#567000$$$", loc, "{:$<16.6Lf}", F(1.234567e3));
+    test("$$$1_23_4#567000", loc, "{:$>16.6Lf}", F(1.234567e3));
+    test("$1_23_4#567000$$", loc, "{:$^16.6Lf}", F(1.234567e3));
+    test("0001_23_4#567000", loc, "{:016.6Lf}", F(1.234567e3));
+    test("-1_23_4#567000$$$", loc, "{:$<17.6Lf}", F(-1.234567e3));
+    test("$$$-1_23_4#567000", loc, "{:$>17.6Lf}", F(-1.234567e3));
+    test("$-1_23_4#567000$$", loc, "{:$^17.6Lf}", F(-1.234567e3));
+    test("-0001_23_4#567000", loc, "{:017.6Lf}", F(-1.234567e3));
+  }
+}
+
+template <class F>
+static void test_floating_point_fixed_upper_case() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Basic ***
+  std::locale::global(en_US);
+  test("0.000001", "{:.6Lf}", F(1.234567e-6));
+  test("0.000012", "{:.6Lf}", F(1.234567e-5));
+  test("0.000123", "{:.6Lf}", F(1.234567e-4));
+  test("0.001235", "{:.6Lf}", F(1.234567e-3));
+  test("0.012346", "{:.6Lf}", F(1.234567e-2));
+  test("0.123457", "{:.6Lf}", F(1.234567e-1));
+  test("1.234567", "{:.6Lf}", F(1.234567e0));
+  test("12.345670", "{:.6Lf}", F(1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("123.456700", "{:.6Lf}", F(1.234567e2));
+    test("1,234.567000", "{:.6Lf}", F(1.234567e3));
+    test("12,345.670000", "{:.6Lf}", F(1.234567e4));
+    test("123,456.700000", "{:.6Lf}", F(1.234567e5));
+    test("1,234,567.000000", "{:.6Lf}", F(1.234567e6));
+    test("12,345,670.000000", "{:.6Lf}", F(1.234567e7));
+    test("123,456,700,000,000,000,000.000000", "{:.6Lf}", F(1.234567e20));
+  }
+  test("-0.000001", "{:.6Lf}", F(-1.234567e-6));
+  test("-0.000012", "{:.6Lf}", F(-1.234567e-5));
+  test("-0.000123", "{:.6Lf}", F(-1.234567e-4));
+  test("-0.001235", "{:.6Lf}", F(-1.234567e-3));
+  test("-0.012346", "{:.6Lf}", F(-1.234567e-2));
+  test("-0.123457", "{:.6Lf}", F(-1.234567e-1));
+  test("-1.234567", "{:.6Lf}", F(-1.234567e0));
+  test("-12.345670", "{:.6Lf}", F(-1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("-123.456700", "{:.6Lf}", F(-1.234567e2));
+    test("-1,234.567000", "{:.6Lf}", F(-1.234567e3));
+    test("-12,345.670000", "{:.6Lf}", F(-1.234567e4));
+    test("-123,456.700000", "{:.6Lf}", F(-1.234567e5));
+    test("-1,234,567.000000", "{:.6Lf}", F(-1.234567e6));
+    test("-12,345,670.000000", "{:.6Lf}", F(-1.234567e7));
+    test("-123,456,700,000,000,000,000.000000", "{:.6Lf}", F(-1.234567e20));
+  }
+
+  std::locale::global(loc);
+  test("0#000001", "{:.6Lf}", F(1.234567e-6));
+  test("0#000012", "{:.6Lf}", F(1.234567e-5));
+  test("0#000123", "{:.6Lf}", F(1.234567e-4));
+  test("0#001235", "{:.6Lf}", F(1.234567e-3));
+  test("0#012346", "{:.6Lf}", F(1.234567e-2));
+  test("0#123457", "{:.6Lf}", F(1.234567e-1));
+  test("1#234567", "{:.6Lf}", F(1.234567e0));
+  test("1_2#345670", "{:.6Lf}", F(1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("12_3#456700", "{:.6Lf}", F(1.234567e2));
+    test("1_23_4#567000", "{:.6Lf}", F(1.234567e3));
+    test("12_34_5#670000", "{:.6Lf}", F(1.234567e4));
+    test("123_45_6#700000", "{:.6Lf}", F(1.234567e5));
+    test("1_234_56_7#000000", "{:.6Lf}", F(1.234567e6));
+    test("12_345_67_0#000000", "{:.6Lf}", F(1.234567e7));
+    test("1_2_3_4_5_6_7_0_0_0_0_0_0_00_000_00_0#000000", "{:.6Lf}", F(1.234567e20));
+  }
+  test("-0#000001", "{:.6Lf}", F(-1.234567e-6));
+  test("-0#000012", "{:.6Lf}", F(-1.234567e-5));
+  test("-0#000123", "{:.6Lf}", F(-1.234567e-4));
+  test("-0#001235", "{:.6Lf}", F(-1.234567e-3));
+  test("-0#012346", "{:.6Lf}", F(-1.234567e-2));
+  test("-0#123457", "{:.6Lf}", F(-1.234567e-1));
+  test("-1#234567", "{:.6Lf}", F(-1.234567e0));
+  test("-1_2#345670", "{:.6Lf}", F(-1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("-12_3#456700", "{:.6Lf}", F(-1.234567e2));
+    test("-1_23_4#567000", "{:.6Lf}", F(-1.234567e3));
+    test("-12_34_5#670000", "{:.6Lf}", F(-1.234567e4));
+    test("-123_45_6#700000", "{:.6Lf}", F(-1.234567e5));
+    test("-1_234_56_7#000000", "{:.6Lf}", F(-1.234567e6));
+    test("-12_345_67_0#000000", "{:.6Lf}", F(-1.234567e7));
+    test("-1_2_3_4_5_6_7_0_0_0_0_0_0_00_000_00_0#000000", "{:.6Lf}", F(-1.234567e20));
+  }
+
+  test("0.000001", en_US, "{:.6Lf}", F(1.234567e-6));
+  test("0.000012", en_US, "{:.6Lf}", F(1.234567e-5));
+  test("0.000123", en_US, "{:.6Lf}", F(1.234567e-4));
+  test("0.001235", en_US, "{:.6Lf}", F(1.234567e-3));
+  test("0.012346", en_US, "{:.6Lf}", F(1.234567e-2));
+  test("0.123457", en_US, "{:.6Lf}", F(1.234567e-1));
+  test("1.234567", en_US, "{:.6Lf}", F(1.234567e0));
+  test("12.345670", en_US, "{:.6Lf}", F(1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("123.456700", en_US, "{:.6Lf}", F(1.234567e2));
+    test("1,234.567000", en_US, "{:.6Lf}", F(1.234567e3));
+    test("12,345.670000", en_US, "{:.6Lf}", F(1.234567e4));
+    test("123,456.700000", en_US, "{:.6Lf}", F(1.234567e5));
+    test("1,234,567.000000", en_US, "{:.6Lf}", F(1.234567e6));
+    test("12,345,670.000000", en_US, "{:.6Lf}", F(1.234567e7));
+    test("123,456,700,000,000,000,000.000000", en_US, "{:.6Lf}", F(1.234567e20));
+  }
+  test("-0.000001", en_US, "{:.6Lf}", F(-1.234567e-6));
+  test("-0.000012", en_US, "{:.6Lf}", F(-1.234567e-5));
+  test("-0.000123", en_US, "{:.6Lf}", F(-1.234567e-4));
+  test("-0.001235", en_US, "{:.6Lf}", F(-1.234567e-3));
+  test("-0.012346", en_US, "{:.6Lf}", F(-1.234567e-2));
+  test("-0.123457", en_US, "{:.6Lf}", F(-1.234567e-1));
+  test("-1.234567", en_US, "{:.6Lf}", F(-1.234567e0));
+  test("-12.345670", en_US, "{:.6Lf}", F(-1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("-123.456700", en_US, "{:.6Lf}", F(-1.234567e2));
+    test("-1,234.567000", en_US, "{:.6Lf}", F(-1.234567e3));
+    test("-12,345.670000", en_US, "{:.6Lf}", F(-1.234567e4));
+    test("-123,456.700000", en_US, "{:.6Lf}", F(-1.234567e5));
+    test("-1,234,567.000000", en_US, "{:.6Lf}", F(-1.234567e6));
+    test("-12,345,670.000000", en_US, "{:.6Lf}", F(-1.234567e7));
+    test("-123,456,700,000,000,000,000.000000", en_US, "{:.6Lf}", F(-1.234567e20));
+  }
+
+  std::locale::global(en_US);
+  test("0#000001", loc, "{:.6Lf}", F(1.234567e-6));
+  test("0#000012", loc, "{:.6Lf}", F(1.234567e-5));
+  test("0#000123", loc, "{:.6Lf}", F(1.234567e-4));
+  test("0#001235", loc, "{:.6Lf}", F(1.234567e-3));
+  test("0#012346", loc, "{:.6Lf}", F(1.234567e-2));
+  test("0#123457", loc, "{:.6Lf}", F(1.234567e-1));
+  test("1#234567", loc, "{:.6Lf}", F(1.234567e0));
+  test("1_2#345670", loc, "{:.6Lf}", F(1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("12_3#456700", loc, "{:.6Lf}", F(1.234567e2));
+    test("1_23_4#567000", loc, "{:.6Lf}", F(1.234567e3));
+    test("12_34_5#670000", loc, "{:.6Lf}", F(1.234567e4));
+    test("123_45_6#700000", loc, "{:.6Lf}", F(1.234567e5));
+    test("1_234_56_7#000000", loc, "{:.6Lf}", F(1.234567e6));
+    test("12_345_67_0#000000", loc, "{:.6Lf}", F(1.234567e7));
+    test("1_2_3_4_5_6_7_0_0_0_0_0_0_00_000_00_0#000000", loc, "{:.6Lf}", F(1.234567e20));
+  }
+  test("-0#000001", loc, "{:.6Lf}", F(-1.234567e-6));
+  test("-0#000012", loc, "{:.6Lf}", F(-1.234567e-5));
+  test("-0#000123", loc, "{:.6Lf}", F(-1.234567e-4));
+  test("-0#001235", loc, "{:.6Lf}", F(-1.234567e-3));
+  test("-0#012346", loc, "{:.6Lf}", F(-1.234567e-2));
+  test("-0#123457", loc, "{:.6Lf}", F(-1.234567e-1));
+  test("-1#234567", loc, "{:.6Lf}", F(-1.234567e0));
+  test("-1_2#345670", loc, "{:.6Lf}", F(-1.234567e1));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("-12_3#456700", loc, "{:.6Lf}", F(-1.234567e2));
+    test("-1_23_4#567000", loc, "{:.6Lf}", F(-1.234567e3));
+    test("-12_34_5#670000", loc, "{:.6Lf}", F(-1.234567e4));
+    test("-123_45_6#700000", loc, "{:.6Lf}", F(-1.234567e5));
+    test("-1_234_56_7#000000", loc, "{:.6Lf}", F(-1.234567e6));
+    test("-12_345_67_0#000000", loc, "{:.6Lf}", F(-1.234567e7));
+    test("-1_2_3_4_5_6_7_0_0_0_0_0_0_00_000_00_0#000000", loc, "{:.6Lf}", F(-1.234567e20));
+  }
+
+  // *** Fill, align, zero padding ***
+  if constexpr (sizeof(F) > sizeof(float)) {
+    std::locale::global(en_US);
+    test("1,234.567000$$$", "{:$<15.6Lf}", F(1.234567e3));
+    test("$$$1,234.567000", "{:$>15.6Lf}", F(1.234567e3));
+    test("$1,234.567000$$", "{:$^15.6Lf}", F(1.234567e3));
+    test("0001,234.567000", "{:015.6Lf}", F(1.234567e3));
+    test("-1,234.567000$$$", "{:$<16.6Lf}", F(-1.234567e3));
+    test("$$$-1,234.567000", "{:$>16.6Lf}", F(-1.234567e3));
+    test("$-1,234.567000$$", "{:$^16.6Lf}", F(-1.234567e3));
+    test("-0001,234.567000", "{:016.6Lf}", F(-1.234567e3));
+
+    std::locale::global(loc);
+    test("1_23_4#567000$$$", "{:$<16.6Lf}", F(1.234567e3));
+    test("$$$1_23_4#567000", "{:$>16.6Lf}", F(1.234567e3));
+    test("$1_23_4#567000$$", "{:$^16.6Lf}", F(1.234567e3));
+    test("0001_23_4#567000", "{:016.6Lf}", F(1.234567e3));
+    test("-1_23_4#567000$$$", "{:$<17.6Lf}", F(-1.234567e3));
+    test("$$$-1_23_4#567000", "{:$>17.6Lf}", F(-1.234567e3));
+    test("$-1_23_4#567000$$", "{:$^17.6Lf}", F(-1.234567e3));
+    test("-0001_23_4#567000", "{:017.6Lf}", F(-1.234567e3));
+
+    test("1,234.567000$$$", en_US, "{:$<15.6Lf}", F(1.234567e3));
+    test("$$$1,234.567000", en_US, "{:$>15.6Lf}", F(1.234567e3));
+    test("$1,234.567000$$", en_US, "{:$^15.6Lf}", F(1.234567e3));
+    test("0001,234.567000", en_US, "{:015.6Lf}", F(1.234567e3));
+    test("-1,234.567000$$$", en_US, "{:$<16.6Lf}", F(-1.234567e3));
+    test("$$$-1,234.567000", en_US, "{:$>16.6Lf}", F(-1.234567e3));
+    test("$-1,234.567000$$", en_US, "{:$^16.6Lf}", F(-1.234567e3));
+    test("-0001,234.567000", en_US, "{:016.6Lf}", F(-1.234567e3));
+
+    std::locale::global(en_US);
+    test("1_23_4#567000$$$", loc, "{:$<16.6Lf}", F(1.234567e3));
+    test("$$$1_23_4#567000", loc, "{:$>16.6Lf}", F(1.234567e3));
+    test("$1_23_4#567000$$", loc, "{:$^16.6Lf}", F(1.234567e3));
+    test("0001_23_4#567000", loc, "{:016.6Lf}", F(1.234567e3));
+    test("-1_23_4#567000$$$", loc, "{:$<17.6Lf}", F(-1.234567e3));
+    test("$$$-1_23_4#567000", loc, "{:$>17.6Lf}", F(-1.234567e3));
+    test("$-1_23_4#567000$$", loc, "{:$^17.6Lf}", F(-1.234567e3));
+    test("-0001_23_4#567000", loc, "{:017.6Lf}", F(-1.234567e3));
+  }
+}
+
+template <class F>
+static void test_floating_point_general_lower_case() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Basic ***
+  std::locale::global(en_US);
+  test("1.23457e-06", "{:.6Lg}", F(1.234567e-6));
+  test("1.23457e-05", "{:.6Lg}", F(1.234567e-5));
+  test("0.000123457", "{:.6Lg}", F(1.234567e-4));
+  test("0.00123457", "{:.6Lg}", F(1.234567e-3));
+  test("0.0123457", "{:.6Lg}", F(1.234567e-2));
+  test("0.123457", "{:.6Lg}", F(1.234567e-1));
+  test("1.23457", "{:.6Lg}", F(1.234567e0));
+  test("12.3457", "{:.6Lg}", F(1.234567e1));
+  test("123.457", "{:.6Lg}", F(1.234567e2));
+  test("1,234.57", "{:.6Lg}", F(1.234567e3));
+  test("12,345.7", "{:.6Lg}", F(1.234567e4));
+  test("123,457", "{:.6Lg}", F(1.234567e5));
+  test("1.23457e+06", "{:.6Lg}", F(1.234567e6));
+  test("1.23457e+07", "{:.6Lg}", F(1.234567e7));
+  test("-1.23457e-06", "{:.6Lg}", F(-1.234567e-6));
+  test("-1.23457e-05", "{:.6Lg}", F(-1.234567e-5));
+  test("-0.000123457", "{:.6Lg}", F(-1.234567e-4));
+  test("-0.00123457", "{:.6Lg}", F(-1.234567e-3));
+  test("-0.0123457", "{:.6Lg}", F(-1.234567e-2));
+  test("-0.123457", "{:.6Lg}", F(-1.234567e-1));
+  test("-1.23457", "{:.6Lg}", F(-1.234567e0));
+  test("-12.3457", "{:.6Lg}", F(-1.234567e1));
+  test("-123.457", "{:.6Lg}", F(-1.234567e2));
+  test("-1,234.57", "{:.6Lg}", F(-1.234567e3));
+  test("-12,345.7", "{:.6Lg}", F(-1.234567e4));
+  test("-123,457", "{:.6Lg}", F(-1.234567e5));
+  test("-1.23457e+06", "{:.6Lg}", F(-1.234567e6));
+  test("-1.23457e+07", "{:.6Lg}", F(-1.234567e7));
+
+  std::locale::global(loc);
+  test("1#23457e-06", "{:.6Lg}", F(1.234567e-6));
+  test("1#23457e-05", "{:.6Lg}", F(1.234567e-5));
+  test("0#000123457", "{:.6Lg}", F(1.234567e-4));
+  test("0#00123457", "{:.6Lg}", F(1.234567e-3));
+  test("0#0123457", "{:.6Lg}", F(1.234567e-2));
+  test("0#123457", "{:.6Lg}", F(1.234567e-1));
+  test("1#23457", "{:.6Lg}", F(1.234567e0));
+  test("1_2#3457", "{:.6Lg}", F(1.234567e1));
+  test("12_3#457", "{:.6Lg}", F(1.234567e2));
+  test("1_23_4#57", "{:.6Lg}", F(1.234567e3));
+  test("12_34_5#7", "{:.6Lg}", F(1.234567e4));
+  test("123_45_7", "{:.6Lg}", F(1.234567e5));
+  test("1#23457e+06", "{:.6Lg}", F(1.234567e6));
+  test("1#23457e+07", "{:.6Lg}", F(1.234567e7));
+  test("-1#23457e-06", "{:.6Lg}", F(-1.234567e-6));
+  test("-1#23457e-05", "{:.6Lg}", F(-1.234567e-5));
+  test("-0#000123457", "{:.6Lg}", F(-1.234567e-4));
+  test("-0#00123457", "{:.6Lg}", F(-1.234567e-3));
+  test("-0#0123457", "{:.6Lg}", F(-1.234567e-2));
+  test("-0#123457", "{:.6Lg}", F(-1.234567e-1));
+  test("-1#23457", "{:.6Lg}", F(-1.234567e0));
+  test("-1_2#3457", "{:.6Lg}", F(-1.234567e1));
+  test("-12_3#457", "{:.6Lg}", F(-1.234567e2));
+  test("-1_23_4#57", "{:.6Lg}", F(-1.234567e3));
+  test("-12_34_5#7", "{:.6Lg}", F(-1.234567e4));
+  test("-123_45_7", "{:.6Lg}", F(-1.234567e5));
+  test("-1#23457e+06", "{:.6Lg}", F(-1.234567e6));
+  test("-1#23457e+07", "{:.6Lg}", F(-1.234567e7));
+
+  test("1.23457e-06", en_US, "{:.6Lg}", F(1.234567e-6));
+  test("1.23457e-05", en_US, "{:.6Lg}", F(1.234567e-5));
+  test("0.000123457", en_US, "{:.6Lg}", F(1.234567e-4));
+  test("0.00123457", en_US, "{:.6Lg}", F(1.234567e-3));
+  test("0.0123457", en_US, "{:.6Lg}", F(1.234567e-2));
+  test("0.123457", en_US, "{:.6Lg}", F(1.234567e-1));
+  test("1.23457", en_US, "{:.6Lg}", F(1.234567e0));
+  test("12.3457", en_US, "{:.6Lg}", F(1.234567e1));
+  test("123.457", en_US, "{:.6Lg}", F(1.234567e2));
+  test("1,234.57", en_US, "{:.6Lg}", F(1.234567e3));
+  test("12,345.7", en_US, "{:.6Lg}", F(1.234567e4));
+  test("123,457", en_US, "{:.6Lg}", F(1.234567e5));
+  test("1.23457e+06", en_US, "{:.6Lg}", F(1.234567e6));
+  test("1.23457e+07", en_US, "{:.6Lg}", F(1.234567e7));
+  test("-1.23457e-06", en_US, "{:.6Lg}", F(-1.234567e-6));
+  test("-1.23457e-05", en_US, "{:.6Lg}", F(-1.234567e-5));
+  test("-0.000123457", en_US, "{:.6Lg}", F(-1.234567e-4));
+  test("-0.00123457", en_US, "{:.6Lg}", F(-1.234567e-3));
+  test("-0.0123457", en_US, "{:.6Lg}", F(-1.234567e-2));
+  test("-0.123457", en_US, "{:.6Lg}", F(-1.234567e-1));
+  test("-1.23457", en_US, "{:.6Lg}", F(-1.234567e0));
+  test("-12.3457", en_US, "{:.6Lg}", F(-1.234567e1));
+  test("-123.457", en_US, "{:.6Lg}", F(-1.234567e2));
+  test("-1,234.57", en_US, "{:.6Lg}", F(-1.234567e3));
+  test("-12,345.7", en_US, "{:.6Lg}", F(-1.234567e4));
+  test("-123,457", en_US, "{:.6Lg}", F(-1.234567e5));
+  test("-1.23457e+06", en_US, "{:.6Lg}", F(-1.234567e6));
+  test("-1.23457e+07", en_US, "{:.6Lg}", F(-1.234567e7));
+
+  std::locale::global(en_US);
+  test("1#23457e-06", loc, "{:.6Lg}", F(1.234567e-6));
+  test("1#23457e-05", loc, "{:.6Lg}", F(1.234567e-5));
+  test("0#000123457", loc, "{:.6Lg}", F(1.234567e-4));
+  test("0#00123457", loc, "{:.6Lg}", F(1.234567e-3));
+  test("0#0123457", loc, "{:.6Lg}", F(1.234567e-2));
+  test("0#123457", loc, "{:.6Lg}", F(1.234567e-1));
+  test("1#23457", loc, "{:.6Lg}", F(1.234567e0));
+  test("1_2#3457", loc, "{:.6Lg}", F(1.234567e1));
+  test("12_3#457", loc, "{:.6Lg}", F(1.234567e2));
+  test("1_23_4#57", loc, "{:.6Lg}", F(1.234567e3));
+  test("12_34_5#7", loc, "{:.6Lg}", F(1.234567e4));
+  test("123_45_7", loc, "{:.6Lg}", F(1.234567e5));
+  test("1#23457e+06", loc, "{:.6Lg}", F(1.234567e6));
+  test("1#23457e+07", loc, "{:.6Lg}", F(1.234567e7));
+  test("-1#23457e-06", loc, "{:.6Lg}", F(-1.234567e-6));
+  test("-1#23457e-05", loc, "{:.6Lg}", F(-1.234567e-5));
+  test("-0#000123457", loc, "{:.6Lg}", F(-1.234567e-4));
+  test("-0#00123457", loc, "{:.6Lg}", F(-1.234567e-3));
+  test("-0#0123457", loc, "{:.6Lg}", F(-1.234567e-2));
+  test("-0#123457", loc, "{:.6Lg}", F(-1.234567e-1));
+  test("-1#23457", loc, "{:.6Lg}", F(-1.234567e0));
+  test("-1_2#3457", loc, "{:.6Lg}", F(-1.234567e1));
+  test("-12_3#457", loc, "{:.6Lg}", F(-1.234567e2));
+  test("-1_23_4#57", loc, "{:.6Lg}", F(-1.234567e3));
+  test("-12_34_5#7", loc, "{:.6Lg}", F(-1.234567e4));
+  test("-123_45_7", loc, "{:.6Lg}", F(-1.234567e5));
+  test("-1#23457e+06", loc, "{:.6Lg}", F(-1.234567e6));
+  test("-1#23457e+07", loc, "{:.6Lg}", F(-1.234567e7));
+
+  // *** Fill, align, zero padding ***
+  std::locale::global(en_US);
+  test("1,234.57$$$", "{:$<11.6Lg}", F(1.234567e3));
+  test("$$$1,234.57", "{:$>11.6Lg}", F(1.234567e3));
+  test("$1,234.57$$", "{:$^11.6Lg}", F(1.234567e3));
+  test("0001,234.57", "{:011.6Lg}", F(1.234567e3));
+  test("-1,234.57$$$", "{:$<12.6Lg}", F(-1.234567e3));
+  test("$$$-1,234.57", "{:$>12.6Lg}", F(-1.234567e3));
+  test("$-1,234.57$$", "{:$^12.6Lg}", F(-1.234567e3));
+  test("-0001,234.57", "{:012.6Lg}", F(-1.234567e3));
+
+  std::locale::global(loc);
+  test("1_23_4#57$$$", "{:$<12.6Lg}", F(1.234567e3));
+  test("$$$1_23_4#57", "{:$>12.6Lg}", F(1.234567e3));
+  test("$1_23_4#57$$", "{:$^12.6Lg}", F(1.234567e3));
+  test("0001_23_4#57", "{:012.6Lg}", F(1.234567e3));
+  test("-1_23_4#57$$$", "{:$<13.6Lg}", F(-1.234567e3));
+  test("$$$-1_23_4#57", "{:$>13.6Lg}", F(-1.234567e3));
+  test("$-1_23_4#57$$", "{:$^13.6Lg}", F(-1.234567e3));
+  test("-0001_23_4#57", "{:013.6Lg}", F(-1.234567e3));
+
+  test("1,234.57$$$", en_US, "{:$<11.6Lg}", F(1.234567e3));
+  test("$$$1,234.57", en_US, "{:$>11.6Lg}", F(1.234567e3));
+  test("$1,234.57$$", en_US, "{:$^11.6Lg}", F(1.234567e3));
+  test("0001,234.57", en_US, "{:011.6Lg}", F(1.234567e3));
+  test("-1,234.57$$$", en_US, "{:$<12.6Lg}", F(-1.234567e3));
+  test("$$$-1,234.57", en_US, "{:$>12.6Lg}", F(-1.234567e3));
+  test("$-1,234.57$$", en_US, "{:$^12.6Lg}", F(-1.234567e3));
+  test("-0001,234.57", en_US, "{:012.6Lg}", F(-1.234567e3));
+
+  std::locale::global(en_US);
+  test("1_23_4#57$$$", loc, "{:$<12.6Lg}", F(1.234567e3));
+  test("$$$1_23_4#57", loc, "{:$>12.6Lg}", F(1.234567e3));
+  test("$1_23_4#57$$", loc, "{:$^12.6Lg}", F(1.234567e3));
+  test("0001_23_4#57", loc, "{:012.6Lg}", F(1.234567e3));
+  test("-1_23_4#57$$$", loc, "{:$<13.6Lg}", F(-1.234567e3));
+  test("$$$-1_23_4#57", loc, "{:$>13.6Lg}", F(-1.234567e3));
+  test("$-1_23_4#57$$", loc, "{:$^13.6Lg}", F(-1.234567e3));
+  test("-0001_23_4#57", loc, "{:013.6Lg}", F(-1.234567e3));
+}
+
+template <class F>
+static void test_floating_point_general_upper_case() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Basic ***
+  std::locale::global(en_US);
+  test("1.23457E-06", "{:.6LG}", F(1.234567e-6));
+  test("1.23457E-05", "{:.6LG}", F(1.234567e-5));
+  test("0.000123457", "{:.6LG}", F(1.234567e-4));
+  test("0.00123457", "{:.6LG}", F(1.234567e-3));
+  test("0.0123457", "{:.6LG}", F(1.234567e-2));
+  test("0.123457", "{:.6LG}", F(1.234567e-1));
+  test("1.23457", "{:.6LG}", F(1.234567e0));
+  test("12.3457", "{:.6LG}", F(1.234567e1));
+  test("123.457", "{:.6LG}", F(1.234567e2));
+  test("1,234.57", "{:.6LG}", F(1.234567e3));
+  test("12,345.7", "{:.6LG}", F(1.234567e4));
+  test("123,457", "{:.6LG}", F(1.234567e5));
+  test("1.23457E+06", "{:.6LG}", F(1.234567e6));
+  test("1.23457E+07", "{:.6LG}", F(1.234567e7));
+  test("-1.23457E-06", "{:.6LG}", F(-1.234567e-6));
+  test("-1.23457E-05", "{:.6LG}", F(-1.234567e-5));
+  test("-0.000123457", "{:.6LG}", F(-1.234567e-4));
+  test("-0.00123457", "{:.6LG}", F(-1.234567e-3));
+  test("-0.0123457", "{:.6LG}", F(-1.234567e-2));
+  test("-0.123457", "{:.6LG}", F(-1.234567e-1));
+  test("-1.23457", "{:.6LG}", F(-1.234567e0));
+  test("-12.3457", "{:.6LG}", F(-1.234567e1));
+  test("-123.457", "{:.6LG}", F(-1.234567e2));
+  test("-1,234.57", "{:.6LG}", F(-1.234567e3));
+  test("-12,345.7", "{:.6LG}", F(-1.234567e4));
+  test("-123,457", "{:.6LG}", F(-1.234567e5));
+  test("-1.23457E+06", "{:.6LG}", F(-1.234567e6));
+  test("-1.23457E+07", "{:.6LG}", F(-1.234567e7));
+
+  std::locale::global(loc);
+  test("1#23457E-06", "{:.6LG}", F(1.234567e-6));
+  test("1#23457E-05", "{:.6LG}", F(1.234567e-5));
+  test("0#000123457", "{:.6LG}", F(1.234567e-4));
+  test("0#00123457", "{:.6LG}", F(1.234567e-3));
+  test("0#0123457", "{:.6LG}", F(1.234567e-2));
+  test("0#123457", "{:.6LG}", F(1.234567e-1));
+  test("1#23457", "{:.6LG}", F(1.234567e0));
+  test("1_2#3457", "{:.6LG}", F(1.234567e1));
+  test("12_3#457", "{:.6LG}", F(1.234567e2));
+  test("1_23_4#57", "{:.6LG}", F(1.234567e3));
+  test("12_34_5#7", "{:.6LG}", F(1.234567e4));
+  test("123_45_7", "{:.6LG}", F(1.234567e5));
+  test("1#23457E+06", "{:.6LG}", F(1.234567e6));
+  test("1#23457E+07", "{:.6LG}", F(1.234567e7));
+  test("-1#23457E-06", "{:.6LG}", F(-1.234567e-6));
+  test("-1#23457E-05", "{:.6LG}", F(-1.234567e-5));
+  test("-0#000123457", "{:.6LG}", F(-1.234567e-4));
+  test("-0#00123457", "{:.6LG}", F(-1.234567e-3));
+  test("-0#0123457", "{:.6LG}", F(-1.234567e-2));
+  test("-0#123457", "{:.6LG}", F(-1.234567e-1));
+  test("-1#23457", "{:.6LG}", F(-1.234567e0));
+  test("-1_2#3457", "{:.6LG}", F(-1.234567e1));
+  test("-12_3#457", "{:.6LG}", F(-1.234567e2));
+  test("-1_23_4#57", "{:.6LG}", F(-1.234567e3));
+  test("-12_34_5#7", "{:.6LG}", F(-1.234567e4));
+  test("-123_45_7", "{:.6LG}", F(-1.234567e5));
+  test("-1#23457E+06", "{:.6LG}", F(-1.234567e6));
+  test("-1#23457E+07", "{:.6LG}", F(-1.234567e7));
+
+  test("1.23457E-06", en_US, "{:.6LG}", F(1.234567e-6));
+  test("1.23457E-05", en_US, "{:.6LG}", F(1.234567e-5));
+  test("0.000123457", en_US, "{:.6LG}", F(1.234567e-4));
+  test("0.00123457", en_US, "{:.6LG}", F(1.234567e-3));
+  test("0.0123457", en_US, "{:.6LG}", F(1.234567e-2));
+  test("0.123457", en_US, "{:.6LG}", F(1.234567e-1));
+  test("1.23457", en_US, "{:.6LG}", F(1.234567e0));
+  test("12.3457", en_US, "{:.6LG}", F(1.234567e1));
+  test("123.457", en_US, "{:.6LG}", F(1.234567e2));
+  test("1,234.57", en_US, "{:.6LG}", F(1.234567e3));
+  test("12,345.7", en_US, "{:.6LG}", F(1.234567e4));
+  test("123,457", en_US, "{:.6LG}", F(1.234567e5));
+  test("1.23457E+06", en_US, "{:.6LG}", F(1.234567e6));
+  test("1.23457E+07", en_US, "{:.6LG}", F(1.234567e7));
+  test("-1.23457E-06", en_US, "{:.6LG}", F(-1.234567e-6));
+  test("-1.23457E-05", en_US, "{:.6LG}", F(-1.234567e-5));
+  test("-0.000123457", en_US, "{:.6LG}", F(-1.234567e-4));
+  test("-0.00123457", en_US, "{:.6LG}", F(-1.234567e-3));
+  test("-0.0123457", en_US, "{:.6LG}", F(-1.234567e-2));
+  test("-0.123457", en_US, "{:.6LG}", F(-1.234567e-1));
+  test("-1.23457", en_US, "{:.6LG}", F(-1.234567e0));
+  test("-12.3457", en_US, "{:.6LG}", F(-1.234567e1));
+  test("-123.457", en_US, "{:.6LG}", F(-1.234567e2));
+  test("-1,234.57", en_US, "{:.6LG}", F(-1.234567e3));
+  test("-12,345.7", en_US, "{:.6LG}", F(-1.234567e4));
+  test("-123,457", en_US, "{:.6LG}", F(-1.234567e5));
+  test("-1.23457E+06", en_US, "{:.6LG}", F(-1.234567e6));
+  test("-1.23457E+07", en_US, "{:.6LG}", F(-1.234567e7));
+
+  std::locale::global(en_US);
+  test("1#23457E-06", loc, "{:.6LG}", F(1.234567e-6));
+  test("1#23457E-05", loc, "{:.6LG}", F(1.234567e-5));
+  test("0#000123457", loc, "{:.6LG}", F(1.234567e-4));
+  test("0#00123457", loc, "{:.6LG}", F(1.234567e-3));
+  test("0#0123457", loc, "{:.6LG}", F(1.234567e-2));
+  test("0#123457", loc, "{:.6LG}", F(1.234567e-1));
+  test("1#23457", loc, "{:.6LG}", F(1.234567e0));
+  test("1_2#3457", loc, "{:.6LG}", F(1.234567e1));
+  test("12_3#457", loc, "{:.6LG}", F(1.234567e2));
+  test("1_23_4#57", loc, "{:.6LG}", F(1.234567e3));
+  test("12_34_5#7", loc, "{:.6LG}", F(1.234567e4));
+  test("123_45_7", loc, "{:.6LG}", F(1.234567e5));
+  test("1#23457E+06", loc, "{:.6LG}", F(1.234567e6));
+  test("1#23457E+07", loc, "{:.6LG}", F(1.234567e7));
+  test("-1#23457E-06", loc, "{:.6LG}", F(-1.234567e-6));
+  test("-1#23457E-05", loc, "{:.6LG}", F(-1.234567e-5));
+  test("-0#000123457", loc, "{:.6LG}", F(-1.234567e-4));
+  test("-0#00123457", loc, "{:.6LG}", F(-1.234567e-3));
+  test("-0#0123457", loc, "{:.6LG}", F(-1.234567e-2));
+  test("-0#123457", loc, "{:.6LG}", F(-1.234567e-1));
+  test("-1#23457", loc, "{:.6LG}", F(-1.234567e0));
+  test("-1_2#3457", loc, "{:.6LG}", F(-1.234567e1));
+  test("-12_3#457", loc, "{:.6LG}", F(-1.234567e2));
+  test("-1_23_4#57", loc, "{:.6LG}", F(-1.234567e3));
+  test("-12_34_5#7", loc, "{:.6LG}", F(-1.234567e4));
+  test("-123_45_7", loc, "{:.6LG}", F(-1.234567e5));
+  test("-1#23457E+06", loc, "{:.6LG}", F(-1.234567e6));
+  test("-1#23457E+07", loc, "{:.6LG}", F(-1.234567e7));
+
+  // *** Fill, align, zero padding ***
+  std::locale::global(en_US);
+  test("1,234.57$$$", "{:$<11.6LG}", F(1.234567e3));
+  test("$$$1,234.57", "{:$>11.6LG}", F(1.234567e3));
+  test("$1,234.57$$", "{:$^11.6LG}", F(1.234567e3));
+  test("0001,234.57", "{:011.6LG}", F(1.234567e3));
+  test("-1,234.57$$$", "{:$<12.6LG}", F(-1.234567e3));
+  test("$$$-1,234.57", "{:$>12.6LG}", F(-1.234567e3));
+  test("$-1,234.57$$", "{:$^12.6LG}", F(-1.234567e3));
+  test("-0001,234.57", "{:012.6LG}", F(-1.234567e3));
+
+  std::locale::global(loc);
+  test("1_23_4#57$$$", "{:$<12.6LG}", F(1.234567e3));
+  test("$$$1_23_4#57", "{:$>12.6LG}", F(1.234567e3));
+  test("$1_23_4#57$$", "{:$^12.6LG}", F(1.234567e3));
+  test("0001_23_4#57", "{:012.6LG}", F(1.234567e3));
+  test("-1_23_4#57$$$", "{:$<13.6LG}", F(-1.234567e3));
+  test("$$$-1_23_4#57", "{:$>13.6LG}", F(-1.234567e3));
+  test("$-1_23_4#57$$", "{:$^13.6LG}", F(-1.234567e3));
+  test("-0001_23_4#57", "{:013.6LG}", F(-1.234567e3));
+
+  test("1,234.57$$$", en_US, "{:$<11.6LG}", F(1.234567e3));
+  test("$$$1,234.57", en_US, "{:$>11.6LG}", F(1.234567e3));
+  test("$1,234.57$$", en_US, "{:$^11.6LG}", F(1.234567e3));
+  test("0001,234.57", en_US, "{:011.6LG}", F(1.234567e3));
+  test("-1,234.57$$$", en_US, "{:$<12.6LG}", F(-1.234567e3));
+  test("$$$-1,234.57", en_US, "{:$>12.6LG}", F(-1.234567e3));
+  test("$-1,234.57$$", en_US, "{:$^12.6LG}", F(-1.234567e3));
+  test("-0001,234.57", en_US, "{:012.6LG}", F(-1.234567e3));
+
+  std::locale::global(en_US);
+  test("1_23_4#57$$$", loc, "{:$<12.6LG}", F(1.234567e3));
+  test("$$$1_23_4#57", loc, "{:$>12.6LG}", F(1.234567e3));
+  test("$1_23_4#57$$", loc, "{:$^12.6LG}", F(1.234567e3));
+  test("0001_23_4#57", loc, "{:012.6LG}", F(1.234567e3));
+  test("-1_23_4#57$$$", loc, "{:$<13.6LG}", F(-1.234567e3));
+  test("$$$-1_23_4#57", loc, "{:$>13.6LG}", F(-1.234567e3));
+  test("$-1_23_4#57$$", loc, "{:$^13.6LG}", F(-1.234567e3));
+  test("-0001_23_4#57", loc, "{:013.6LG}", F(-1.234567e3));
+}
+
+template <class F>
+static void test_floating_point_default() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Basic ***
+  std::locale::global(en_US);
+  test("1.234567e-06", "{:L}", F(1.234567e-6));
+  test("1.234567e-05", "{:L}", F(1.234567e-5));
+  test("0.0001234567", "{:L}", F(1.234567e-4));
+  test("0.001234567", "{:L}", F(1.234567e-3));
+  test("0.01234567", "{:L}", F(1.234567e-2));
+  test("0.1234567", "{:L}", F(1.234567e-1));
+  test("1.234567", "{:L}", F(1.234567e0));
+  test("12.34567", "{:L}", F(1.234567e1));
+  test("123.4567", "{:L}", F(1.234567e2));
+  test("1,234.567", "{:L}", F(1.234567e3));
+  test("12,345.67", "{:L}", F(1.234567e4));
+  test("123,456.7", "{:L}", F(1.234567e5));
+  test("1,234,567", "{:L}", F(1.234567e6));
+  test("12,345,670", "{:L}", F(1.234567e7));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("123,456,700", "{:L}", F(1.234567e8));
+    test("1,234,567,000", "{:L}", F(1.234567e9));
+    test("12,345,670,000", "{:L}", F(1.234567e10));
+    test("123,456,700,000", "{:L}", F(1.234567e11));
+    test("1.234567e+12", "{:L}", F(1.234567e12));
+    test("1.234567e+13", "{:L}", F(1.234567e13));
+  }
+  test("-1.234567e-06", "{:L}", F(-1.234567e-6));
+  test("-1.234567e-05", "{:L}", F(-1.234567e-5));
+  test("-0.0001234567", "{:L}", F(-1.234567e-4));
+  test("-0.001234567", "{:L}", F(-1.234567e-3));
+  test("-0.01234567", "{:L}", F(-1.234567e-2));
+  test("-0.1234567", "{:L}", F(-1.234567e-1));
+  test("-1.234567", "{:L}", F(-1.234567e0));
+  test("-12.34567", "{:L}", F(-1.234567e1));
+  test("-123.4567", "{:L}", F(-1.234567e2));
+  test("-1,234.567", "{:L}", F(-1.234567e3));
+  test("-12,345.67", "{:L}", F(-1.234567e4));
+  test("-123,456.7", "{:L}", F(-1.234567e5));
+  test("-1,234,567", "{:L}", F(-1.234567e6));
+  test("-12,345,670", "{:L}", F(-1.234567e7));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("-123,456,700", "{:L}", F(-1.234567e8));
+    test("-1,234,567,000", "{:L}", F(-1.234567e9));
+    test("-12,345,670,000", "{:L}", F(-1.234567e10));
+    test("-123,456,700,000", "{:L}", F(-1.234567e11));
+    test("-1.234567e+12", "{:L}", F(-1.234567e12));
+    test("-1.234567e+13", "{:L}", F(-1.234567e13));
+  }
+
+  std::locale::global(loc);
+  test("1#234567e-06", "{:L}", F(1.234567e-6));
+  test("1#234567e-05", "{:L}", F(1.234567e-5));
+  test("0#0001234567", "{:L}", F(1.234567e-4));
+  test("0#001234567", "{:L}", F(1.234567e-3));
+  test("0#01234567", "{:L}", F(1.234567e-2));
+  test("0#1234567", "{:L}", F(1.234567e-1));
+  test("1#234567", "{:L}", F(1.234567e0));
+  test("1_2#34567", "{:L}", F(1.234567e1));
+  test("12_3#4567", "{:L}", F(1.234567e2));
+  test("1_23_4#567", "{:L}", F(1.234567e3));
+  test("12_34_5#67", "{:L}", F(1.234567e4));
+  test("123_45_6#7", "{:L}", F(1.234567e5));
+  test("1_234_56_7", "{:L}", F(1.234567e6));
+  test("12_345_67_0", "{:L}", F(1.234567e7));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("1_23_456_70_0", "{:L}", F(1.234567e8));
+    test("1_2_34_567_00_0", "{:L}", F(1.234567e9));
+    test("1_2_3_45_670_00_0", "{:L}", F(1.234567e10));
+    test("1_2_3_4_56_700_00_0", "{:L}", F(1.234567e11));
+    test("1#234567e+12", "{:L}", F(1.234567e12));
+    test("1#234567e+13", "{:L}", F(1.234567e13));
+  }
+  test("-1#234567e-06", "{:L}", F(-1.234567e-6));
+  test("-1#234567e-05", "{:L}", F(-1.234567e-5));
+  test("-0#0001234567", "{:L}", F(-1.234567e-4));
+  test("-0#001234567", "{:L}", F(-1.234567e-3));
+  test("-0#01234567", "{:L}", F(-1.234567e-2));
+  test("-0#1234567", "{:L}", F(-1.234567e-1));
+  test("-1#234567", "{:L}", F(-1.234567e0));
+  test("-1_2#34567", "{:L}", F(-1.234567e1));
+  test("-12_3#4567", "{:L}", F(-1.234567e2));
+  test("-1_23_4#567", "{:L}", F(-1.234567e3));
+  test("-12_34_5#67", "{:L}", F(-1.234567e4));
+  test("-123_45_6#7", "{:L}", F(-1.234567e5));
+  test("-1_234_56_7", "{:L}", F(-1.234567e6));
+  test("-12_345_67_0", "{:L}", F(-1.234567e7));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("-1_23_456_70_0", "{:L}", F(-1.234567e8));
+    test("-1_2_34_567_00_0", "{:L}", F(-1.234567e9));
+    test("-1_2_3_45_670_00_0", "{:L}", F(-1.234567e10));
+    test("-1_2_3_4_56_700_00_0", "{:L}", F(-1.234567e11));
+    test("-1#234567e+12", "{:L}", F(-1.234567e12));
+    test("-1#234567e+13", "{:L}", F(-1.234567e13));
+  }
+
+  test("1.234567e-06", en_US, "{:L}", F(1.234567e-6));
+  test("1.234567e-05", en_US, "{:L}", F(1.234567e-5));
+  test("0.0001234567", en_US, "{:L}", F(1.234567e-4));
+  test("0.001234567", en_US, "{:L}", F(1.234567e-3));
+  test("0.01234567", en_US, "{:L}", F(1.234567e-2));
+  test("0.1234567", en_US, "{:L}", F(1.234567e-1));
+  test("1.234567", en_US, "{:L}", F(1.234567e0));
+  test("12.34567", en_US, "{:L}", F(1.234567e1));
+  test("123.4567", en_US, "{:L}", F(1.234567e2));
+  test("1,234.567", en_US, "{:L}", F(1.234567e3));
+  test("12,345.67", en_US, "{:L}", F(1.234567e4));
+  test("123,456.7", en_US, "{:L}", F(1.234567e5));
+  test("1,234,567", en_US, "{:L}", F(1.234567e6));
+  test("12,345,670", en_US, "{:L}", F(1.234567e7));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("123,456,700", en_US, "{:L}", F(1.234567e8));
+    test("1,234,567,000", en_US, "{:L}", F(1.234567e9));
+    test("12,345,670,000", en_US, "{:L}", F(1.234567e10));
+    test("123,456,700,000", en_US, "{:L}", F(1.234567e11));
+    test("1.234567e+12", en_US, "{:L}", F(1.234567e12));
+    test("1.234567e+13", en_US, "{:L}", F(1.234567e13));
+  }
+  test("-1.234567e-06", en_US, "{:L}", F(-1.234567e-6));
+  test("-1.234567e-05", en_US, "{:L}", F(-1.234567e-5));
+  test("-0.0001234567", en_US, "{:L}", F(-1.234567e-4));
+  test("-0.001234567", en_US, "{:L}", F(-1.234567e-3));
+  test("-0.01234567", en_US, "{:L}", F(-1.234567e-2));
+  test("-0.1234567", en_US, "{:L}", F(-1.234567e-1));
+  test("-1.234567", en_US, "{:L}", F(-1.234567e0));
+  test("-12.34567", en_US, "{:L}", F(-1.234567e1));
+  test("-123.4567", en_US, "{:L}", F(-1.234567e2));
+  test("-1,234.567", en_US, "{:L}", F(-1.234567e3));
+  test("-12,345.67", en_US, "{:L}", F(-1.234567e4));
+  test("-123,456.7", en_US, "{:L}", F(-1.234567e5));
+  test("-1,234,567", en_US, "{:L}", F(-1.234567e6));
+  test("-12,345,670", en_US, "{:L}", F(-1.234567e7));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("-123,456,700", en_US, "{:L}", F(-1.234567e8));
+    test("-1,234,567,000", en_US, "{:L}", F(-1.234567e9));
+    test("-12,345,670,000", en_US, "{:L}", F(-1.234567e10));
+    test("-123,456,700,000", en_US, "{:L}", F(-1.234567e11));
+    test("-1.234567e+12", en_US, "{:L}", F(-1.234567e12));
+    test("-1.234567e+13", en_US, "{:L}", F(-1.234567e13));
+  }
+
+  std::locale::global(en_US);
+  test("1#234567e-06", loc, "{:L}", F(1.234567e-6));
+  test("1#234567e-05", loc, "{:L}", F(1.234567e-5));
+  test("0#0001234567", loc, "{:L}", F(1.234567e-4));
+  test("0#001234567", loc, "{:L}", F(1.234567e-3));
+  test("0#01234567", loc, "{:L}", F(1.234567e-2));
+  test("0#1234567", loc, "{:L}", F(1.234567e-1));
+  test("1#234567", loc, "{:L}", F(1.234567e0));
+  test("1_2#34567", loc, "{:L}", F(1.234567e1));
+  test("12_3#4567", loc, "{:L}", F(1.234567e2));
+  test("1_23_4#567", loc, "{:L}", F(1.234567e3));
+  test("12_34_5#67", loc, "{:L}", F(1.234567e4));
+  test("123_45_6#7", loc, "{:L}", F(1.234567e5));
+  test("1_234_56_7", loc, "{:L}", F(1.234567e6));
+  test("12_345_67_0", loc, "{:L}", F(1.234567e7));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("1_23_456_70_0", loc, "{:L}", F(1.234567e8));
+    test("1_2_34_567_00_0", loc, "{:L}", F(1.234567e9));
+    test("1_2_3_45_670_00_0", loc, "{:L}", F(1.234567e10));
+    test("1_2_3_4_56_700_00_0", loc, "{:L}", F(1.234567e11));
+    test("1#234567e+12", loc, "{:L}", F(1.234567e12));
+    test("1#234567e+13", loc, "{:L}", F(1.234567e13));
+  }
+  test("-1#234567e-06", loc, "{:L}", F(-1.234567e-6));
+  test("-1#234567e-05", loc, "{:L}", F(-1.234567e-5));
+  test("-0#0001234567", loc, "{:L}", F(-1.234567e-4));
+  test("-0#001234567", loc, "{:L}", F(-1.234567e-3));
+  test("-0#01234567", loc, "{:L}", F(-1.234567e-2));
+  test("-0#1234567", loc, "{:L}", F(-1.234567e-1));
+  test("-1#234567", loc, "{:L}", F(-1.234567e0));
+  test("-1_2#34567", loc, "{:L}", F(-1.234567e1));
+  test("-12_3#4567", loc, "{:L}", F(-1.234567e2));
+  test("-1_23_4#567", loc, "{:L}", F(-1.234567e3));
+  test("-12_34_5#67", loc, "{:L}", F(-1.234567e4));
+  test("-123_45_6#7", loc, "{:L}", F(-1.234567e5));
+  test("-1_234_56_7", loc, "{:L}", F(-1.234567e6));
+  test("-12_345_67_0", loc, "{:L}", F(-1.234567e7));
+  if constexpr (sizeof(F) > sizeof(float)) {
+    test("-1_23_456_70_0", loc, "{:L}", F(-1.234567e8));
+    test("-1_2_34_567_00_0", loc, "{:L}", F(-1.234567e9));
+    test("-1_2_3_45_670_00_0", loc, "{:L}", F(-1.234567e10));
+    test("-1_2_3_4_56_700_00_0", loc, "{:L}", F(-1.234567e11));
+    test("-1#234567e+12", loc, "{:L}", F(-1.234567e12));
+    test("-1#234567e+13", loc, "{:L}", F(-1.234567e13));
+  }
+
+  // *** Fill, align, zero padding ***
+  std::locale::global(en_US);
+  test("1,234.567$$$", "{:$<12L}", F(1.234567e3));
+  test("$$$1,234.567", "{:$>12L}", F(1.234567e3));
+  test("$1,234.567$$", "{:$^12L}", F(1.234567e3));
+  test("0001,234.567", "{:012L}", F(1.234567e3));
+  test("-1,234.567$$$", "{:$<13L}", F(-1.234567e3));
+  test("$$$-1,234.567", "{:$>13L}", F(-1.234567e3));
+  test("$-1,234.567$$", "{:$^13L}", F(-1.234567e3));
+  test("-0001,234.567", "{:013L}", F(-1.234567e3));
+
+  std::locale::global(loc);
+  test("1_23_4#567$$$", "{:$<13L}", F(1.234567e3));
+  test("$$$1_23_4#567", "{:$>13L}", F(1.234567e3));
+  test("$1_23_4#567$$", "{:$^13L}", F(1.234567e3));
+  test("0001_23_4#567", "{:013L}", F(1.234567e3));
+  test("-1_23_4#567$$$", "{:$<14L}", F(-1.234567e3));
+  test("$$$-1_23_4#567", "{:$>14L}", F(-1.234567e3));
+  test("$-1_23_4#567$$", "{:$^14L}", F(-1.234567e3));
+  test("-0001_23_4#567", "{:014L}", F(-1.234567e3));
+
+  test("1,234.567$$$", en_US, "{:$<12L}", F(1.234567e3));
+  test("$$$1,234.567", en_US, "{:$>12L}", F(1.234567e3));
+  test("$1,234.567$$", en_US, "{:$^12L}", F(1.234567e3));
+  test("0001,234.567", en_US, "{:012L}", F(1.234567e3));
+  test("-1,234.567$$$", en_US, "{:$<13L}", F(-1.234567e3));
+  test("$$$-1,234.567", en_US, "{:$>13L}", F(-1.234567e3));
+  test("$-1,234.567$$", en_US, "{:$^13L}", F(-1.234567e3));
+  test("-0001,234.567", en_US, "{:013L}", F(-1.234567e3));
+
+  std::locale::global(en_US);
+  test("1_23_4#567$$$", loc, "{:$<13L}", F(1.234567e3));
+  test("$$$1_23_4#567", loc, "{:$>13L}", F(1.234567e3));
+  test("$1_23_4#567$$", loc, "{:$^13L}", F(1.234567e3));
+  test("0001_23_4#567", loc, "{:013L}", F(1.234567e3));
+  test("-1_23_4#567$$$", loc, "{:$<14L}", F(-1.234567e3));
+  test("$$$-1_23_4#567", loc, "{:$>14L}", F(-1.234567e3));
+  test("$-1_23_4#567$$", loc, "{:$^14L}", F(-1.234567e3));
+  test("-0001_23_4#567", loc, "{:014L}", F(-1.234567e3));
+}
+
+template <class F>
+static void test_floating_point_default_precision() {
+  std::locale loc   = std::locale(std::locale(), new numpunct<char>());
+  std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
+
+  // *** Basic ***
+  std::locale::global(en_US);
+  test("1.23457e-06", "{:.6L}", F(1.234567e-6));
+  test("1.23457e-05", "{:.6L}", F(1.234567e-5));
+  test("0.000123457", "{:.6L}", F(1.234567e-4));
+  test("0.00123457", "{:.6L}", F(1.234567e-3));
+  test("0.0123457", "{:.6L}", F(1.234567e-2));
+  test("0.123457", "{:.6L}", F(1.234567e-1));
+  test("1.23457", "{:.6L}", F(1.234567e0));
+  test("12.3457", "{:.6L}", F(1.234567e1));
+  test("123.457", "{:.6L}", F(1.234567e2));
+  test("1,234.57", "{:.6L}", F(1.234567e3));
+  test("12,345.7", "{:.6L}", F(1.234567e4));
+  test("123,457", "{:.6L}", F(1.234567e5));
+  test("1.23457e+06", "{:.6L}", F(1.234567e6));
+  test("1.23457e+07", "{:.6L}", F(1.234567e7));
+  test("-1.23457e-06", "{:.6L}", F(-1.234567e-6));
+  test("-1.23457e-05", "{:.6L}", F(-1.234567e-5));
+  test("-0.000123457", "{:.6L}", F(-1.234567e-4));
+  test("-0.00123457", "{:.6L}", F(-1.234567e-3));
+  test("-0.0123457", "{:.6L}", F(-1.234567e-2));
+  test("-0.123457", "{:.6L}", F(-1.234567e-1));
+  test("-1.23457", "{:.6L}", F(-1.234567e0));
+  test("-12.3457", "{:.6L}", F(-1.234567e1));
+  test("-123.457", "{:.6L}", F(-1.234567e2));
+  test("-1,234.57", "{:.6L}", F(-1.234567e3));
+  test("-12,345.7", "{:.6L}", F(-1.234567e4));
+  test("-123,457", "{:.6L}", F(-1.234567e5));
+  test("-1.23457e+06", "{:.6L}", F(-1.234567e6));
+  test("-1.23457e+07", "{:.6L}", F(-1.234567e7));
+
+  std::locale::global(loc);
+  test("1#23457e-06", "{:.6L}", F(1.234567e-6));
+  test("1#23457e-05", "{:.6L}", F(1.234567e-5));
+  test("0#000123457", "{:.6L}", F(1.234567e-4));
+  test("0#00123457", "{:.6L}", F(1.234567e-3));
+  test("0#0123457", "{:.6L}", F(1.234567e-2));
+  test("0#123457", "{:.6L}", F(1.234567e-1));
+  test("1#23457", "{:.6L}", F(1.234567e0));
+  test("1_2#3457", "{:.6L}", F(1.234567e1));
+  test("12_3#457", "{:.6L}", F(1.234567e2));
+  test("1_23_4#57", "{:.6L}", F(1.234567e3));
+  test("12_34_5#7", "{:.6L}", F(1.234567e4));
+  test("123_45_7", "{:.6L}", F(1.234567e5));
+  test("1#23457e+06", "{:.6L}", F(1.234567e6));
+  test("1#23457e+07", "{:.6L}", F(1.234567e7));
+  test("-1#23457e-06", "{:.6L}", F(-1.234567e-6));
+  test("-1#23457e-05", "{:.6L}", F(-1.234567e-5));
+  test("-0#000123457", "{:.6L}", F(-1.234567e-4));
+  test("-0#00123457", "{:.6L}", F(-1.234567e-3));
+  test("-0#0123457", "{:.6L}", F(-1.234567e-2));
+  test("-0#123457", "{:.6L}", F(-1.234567e-1));
+  test("-1#23457", "{:.6L}", F(-1.234567e0));
+  test("-1_2#3457", "{:.6L}", F(-1.234567e1));
+  test("-12_3#457", "{:.6L}", F(-1.234567e2));
+  test("-1_23_4#57", "{:.6L}", F(-1.234567e3));
+  test("-12_34_5#7", "{:.6L}", F(-1.234567e4));
+  test("-123_45_7", "{:.6L}", F(-1.234567e5));
+  test("-1#23457e+06", "{:.6L}", F(-1.234567e6));
+  test("-1#23457e+07", "{:.6L}", F(-1.234567e7));
+
+  test("1.23457e-06", en_US, "{:.6L}", F(1.234567e-6));
+  test("1.23457e-05", en_US, "{:.6L}", F(1.234567e-5));
+  test("0.000123457", en_US, "{:.6L}", F(1.234567e-4));
+  test("0.00123457", en_US, "{:.6L}", F(1.234567e-3));
+  test("0.0123457", en_US, "{:.6L}", F(1.234567e-2));
+  test("0.123457", en_US, "{:.6L}", F(1.234567e-1));
+  test("1.23457", en_US, "{:.6L}", F(1.234567e0));
+  test("12.3457", en_US, "{:.6L}", F(1.234567e1));
+  test("123.457", en_US, "{:.6L}", F(1.234567e2));
+  test("1,234.57", en_US, "{:.6L}", F(1.234567e3));
+  test("12,345.7", en_US, "{:.6L}", F(1.234567e4));
+  test("123,457", en_US, "{:.6L}", F(1.234567e5));
+  test("1.23457e+06", en_US, "{:.6L}", F(1.234567e6));
+  test("1.23457e+07", en_US, "{:.6L}", F(1.234567e7));
+  test("-1.23457e-06", en_US, "{:.6L}", F(-1.234567e-6));
+  test("-1.23457e-05", en_US, "{:.6L}", F(-1.234567e-5));
+  test("-0.000123457", en_US, "{:.6L}", F(-1.234567e-4));
+  test("-0.00123457", en_US, "{:.6L}", F(-1.234567e-3));
+  test("-0.0123457", en_US, "{:.6L}", F(-1.234567e-2));
+  test("-0.123457", en_US, "{:.6L}", F(-1.234567e-1));
+  test("-1.23457", en_US, "{:.6L}", F(-1.234567e0));
+  test("-12.3457", en_US, "{:.6L}", F(-1.234567e1));
+  test("-123.457", en_US, "{:.6L}", F(-1.234567e2));
+  test("-1,234.57", en_US, "{:.6L}", F(-1.234567e3));
+  test("-12,345.7", en_US, "{:.6L}", F(-1.234567e4));
+  test("-123,457", en_US, "{:.6L}", F(-1.234567e5));
+  test("-1.23457e+06", en_US, "{:.6L}", F(-1.234567e6));
+  test("-1.23457e+07", en_US, "{:.6L}", F(-1.234567e7));
+
+  std::locale::global(en_US);
+  test("1#23457e-06", loc, "{:.6L}", F(1.234567e-6));
+  test("1#23457e-05", loc, "{:.6L}", F(1.234567e-5));
+  test("0#000123457", loc, "{:.6L}", F(1.234567e-4));
+  test("0#00123457", loc, "{:.6L}", F(1.234567e-3));
+  test("0#0123457", loc, "{:.6L}", F(1.234567e-2));
+  test("0#123457", loc, "{:.6L}", F(1.234567e-1));
+  test("1#23457", loc, "{:.6L}", F(1.234567e0));
+  test("1_2#3457", loc, "{:.6L}", F(1.234567e1));
+  test("12_3#457", loc, "{:.6L}", F(1.234567e2));
+  test("1_23_4#57", loc, "{:.6L}", F(1.234567e3));
+  test("12_34_5#7", loc, "{:.6L}", F(1.234567e4));
+  test("123_45_7", loc, "{:.6L}", F(1.234567e5));
+  test("1#23457e+06", loc, "{:.6L}", F(1.234567e6));
+  test("1#23457e+07", loc, "{:.6L}", F(1.234567e7));
+  test("-1#23457e-06", loc, "{:.6L}", F(-1.234567e-6));
+  test("-1#23457e-05", loc, "{:.6L}", F(-1.234567e-5));
+  test("-0#000123457", loc, "{:.6L}", F(-1.234567e-4));
+  test("-0#00123457", loc, "{:.6L}", F(-1.234567e-3));
+  test("-0#0123457", loc, "{:.6L}", F(-1.234567e-2));
+  test("-0#123457", loc, "{:.6L}", F(-1.234567e-1));
+  test("-1#23457", loc, "{:.6L}", F(-1.234567e0));
+  test("-1_2#3457", loc, "{:.6L}", F(-1.234567e1));
+  test("-12_3#457", loc, "{:.6L}", F(-1.234567e2));
+  test("-1_23_4#57", loc, "{:.6L}", F(-1.234567e3));
+  test("-12_34_5#7", loc, "{:.6L}", F(-1.234567e4));
+  test("-123_45_7", loc, "{:.6L}", F(-1.234567e5));
+  test("-1#23457e+06", loc, "{:.6L}", F(-1.234567e6));
+  test("-1#23457e+07", loc, "{:.6L}", F(-1.234567e7));
+
+  // *** Fill, align, zero padding ***
+  std::locale::global(en_US);
+  test("1,234.57$$$", "{:$<11.6L}", F(1.234567e3));
+  test("$$$1,234.57", "{:$>11.6L}", F(1.234567e3));
+  test("$1,234.57$$", "{:$^11.6L}", F(1.234567e3));
+  test("0001,234.57", "{:011.6L}", F(1.234567e3));
+  test("-1,234.57$$$", "{:$<12.6L}", F(-1.234567e3));
+  test("$$$-1,234.57", "{:$>12.6L}", F(-1.234567e3));
+  test("$-1,234.57$$", "{:$^12.6L}", F(-1.234567e3));
+  test("-0001,234.57", "{:012.6L}", F(-1.234567e3));
+
+  std::locale::global(loc);
+  test("1_23_4#57$$$", "{:$<12.6L}", F(1.234567e3));
+  test("$$$1_23_4#57", "{:$>12.6L}", F(1.234567e3));
+  test("$1_23_4#57$$", "{:$^12.6L}", F(1.234567e3));
+  test("0001_23_4#57", "{:012.6L}", F(1.234567e3));
+  test("-1_23_4#57$$$", "{:$<13.6L}", F(-1.234567e3));
+  test("$$$-1_23_4#57", "{:$>13.6L}", F(-1.234567e3));
+  test("$-1_23_4#57$$", "{:$^13.6L}", F(-1.234567e3));
+  test("-0001_23_4#57", "{:013.6L}", F(-1.234567e3));
+
+  test("1,234.57$$$", en_US, "{:$<11.6L}", F(1.234567e3));
+  test("$$$1,234.57", en_US, "{:$>11.6L}", F(1.234567e3));
+  test("$1,234.57$$", en_US, "{:$^11.6L}", F(1.234567e3));
+  test("0001,234.57", en_US, "{:011.6L}", F(1.234567e3));
+  test("-1,234.57$$$", en_US, "{:$<12.6L}", F(-1.234567e3));
+  test("$$$-1,234.57", en_US, "{:$>12.6L}", F(-1.234567e3));
+  test("$-1,234.57$$", en_US, "{:$^12.6L}", F(-1.234567e3));
+  test("-0001,234.57", en_US, "{:012.6L}", F(-1.234567e3));
+
+  std::locale::global(en_US);
+  test("1_23_4#57$$$", loc, "{:$<12.6L}", F(1.234567e3));
+  test("$$$1_23_4#57", loc, "{:$>12.6L}", F(1.234567e3));
+  test("$1_23_4#57$$", loc, "{:$^12.6L}", F(1.234567e3));
+  test("0001_23_4#57", loc, "{:012.6L}", F(1.234567e3));
+  test("-1_23_4#57$$$", loc, "{:$<13.6L}", F(-1.234567e3));
+  test("$$$-1_23_4#57", loc, "{:$>13.6L}", F(-1.234567e3));
+  test("$-1_23_4#57$$", loc, "{:$^13.6L}", F(-1.234567e3));
+  test("-0001_23_4#57", loc, "{:013.6L}", F(-1.234567e3));
+}
+
+template <class F>
+static void test_floating_point() {
+  test_floating_point_hex_lower_case<F>();
+  test_floating_point_hex_upper_case<F>();
+  test_floating_point_hex_lower_case_precision<F>();
+  test_floating_point_hex_upper_case_precision<F>();
+
+  test_floating_point_scientific_lower_case<F>();
+  test_floating_point_scientific_upper_case<F>();
+
+  test_floating_point_fixed_lower_case<F>();
+  test_floating_point_fixed_upper_case<F>();
+
+  test_floating_point_general_lower_case<F>();
+  test_floating_point_general_upper_case<F>();
+
+  test_floating_point_default<F>();
+  test_floating_point_default_precision<F>();
+}
+
+int main(int, char**) {
+  test_bool();
+  test_integer();
+  test_floating_point<float>();
+  test_floating_point<double>();
+  test_floating_point<long double>();
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print.pass.cpp
new file mode 100644
index 00000000000000..0831ef71076629
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print.pass.cpp
@@ -0,0 +1,193 @@
+//===----------------------------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO PRINT Investigate see https://reviews.llvm.org/D156585
+// UNSUPPORTED: no-filesystem
+
+// XFAIL: availability-fp_to_chars-missing
+// XFAIL: availability-print-missing
+
+// <ostream>
+
+// template<class... Args>
+//   void print(ostream& os, format_string<Args...> fmt, Args&&... args);
+
+// [ostream.formatted.print]/3
+//   If the function is vprint_unicode and os is a stream that refers to
+//   a terminal capable of displaying Unicode which is determined in an
+//   implementation-defined manner, writes out to the terminal using the
+//   native Unicode API;
+// This is tested in
+// test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
+
+#include <cassert>
+#include <ostream>
+#include <sstream>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "print_tests.h"
+#include "test_format_string.h"
+#include "test_macros.h"
+
+auto test_file = []<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
+  std::stringstream sstr;
+  std::print(sstr, fmt, std::forward<Args>(args)...);
+
+  std::string out = sstr.str();
+  TEST_REQUIRE(out == expected,
+               TEST_WRITE_CONCATENATED(
+                   "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+};
+
+auto test_exception = []<class... Args>(std::string_view, std::string_view, Args&&...) {
+  // After P2216 most exceptions thrown by std::format become ill-formed.
+  // Therefore this tests does nothing.
+  // A basic ill-formed test is done in format.verify.cpp
+  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
+};
+// [ostream.formatted.print]/3.2
+//   ...
+//   After constructing a sentry object, the function initializes an automatic variable via
+//     string out = vformat(os.getloc(), fmt, args);
+// This means if both
+// - creating a sentry fails
+// - the formatting fails
+// the first one "wins" and the format_error is not thrown.
+static void test_sentry_failure() {
+  // In order for the creation of a sentry to fail a tied stream's
+  // sync operation should fail.
+  struct sync_failure : public std::basic_streambuf<char> {
+  protected:
+    int virtual sync() { return -1; }
+  };
+  sync_failure buf_tied;
+  std::ostream os_tied(&buf_tied);
+  os_tied.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
+
+  std::stringstream os;
+  os.tie(&os_tied);
+  os.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
+
+  TEST_THROWS_TYPE(std::ios_base::failure, std::print(os, "valid"));
+  os_tied.clear();
+  TEST_THROWS_TYPE(std::ios_base::failure, std::print(os, "throws exception at run-time {0:{0}}", -10));
+
+  os.exceptions(std::stringstream::goodbit);
+  os.setstate(std::stringstream::failbit);
+  std::print(os, "not called when the os.good() is false, so no exception is thrown {0:{0}}", -10);
+}
+
+// [ostream.formatted.print]/3.2
+//   any exception thrown by the call to vformat is propagated without
+//   regard to the value of os.exceptions() and without turning on
+//   ios_base::badbit in the error state of os.
+// Most invalid format strings are checked at compile-time. An invalid
+// value for the width can only be tested run-time.
+static void test_format_exception() {
+  std::stringstream sstr;
+  assert(sstr.good());
+
+  TEST_THROWS_TYPE(std::format_error, std::print(sstr, "no output {0:{0}}", -10));
+  assert(sstr.good());
+  assert(sstr.str().empty());
+
+  sstr.exceptions(std::stringstream::goodbit);
+  TEST_THROWS_TYPE(std::format_error, std::print(sstr, "no output {0:{0}}", -10));
+  assert(sstr.good());
+  assert(sstr.str().empty());
+
+  sstr.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
+  TEST_THROWS_TYPE(std::format_error, std::print(sstr, "no output {0:{0}}", -10));
+  assert(sstr.good());
+  assert(sstr.str().empty());
+}
+
+static void test_write_failure() {
+  // Stream that fails to write a single character.
+  struct overflow_failure : public std::basic_streambuf<char> {
+  protected:
+    int virtual overflow(int) { return std::char_traits<char>::eof(); }
+  };
+  overflow_failure buf;
+  std::ostream os(&buf);
+  os.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
+
+  TEST_THROWS_TYPE(std::ios_base::failure, std::print(os, "valid"));
+  os.clear();
+  // When the parser would directly write to the output instead of
+  // formatting first it would fail writing the first character 't' of
+  // the string and result in a std::ios_base::failure exception.
+  TEST_THROWS_TYPE(std::format_error, std::print(os, "throws exception at run-time {0:{0}}", -10));
+
+  os.exceptions(std::stringstream::goodbit);
+  os.clear();
+  std::print(os, "valid");
+  assert(os.fail());
+}
+
+static void test_stream_formatting() {
+  std::stringstream sstr;
+  auto test = [&]<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
+    sstr.str("");
+    std::print(sstr, fmt, std::forward<Args>(args)...);
+
+    std::string out = sstr.str();
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  };
+
+  test("hello", "{}", "hello");
+
+  sstr.width(10);
+  test("     hello", "{}", "hello");
+
+  sstr.fill('+');
+
+  sstr.width(10);
+  test("+++++hello", "{}", "hello");
+
+  // *** Test embedded NUL character ***
+  using namespace std::literals;
+  sstr.width(15);
+  test("++++hello\0world"sv, "hello{}{}", '\0', "world");
+
+  // *** Test Unicode ***
+  // Streams count code units not code points
+  // 2-byte code points
+  sstr.width(5);
+  test("+++\u00a1", "{}", "\u00a1"); // INVERTED EXCLAMATION MARK
+  sstr.width(5);
+  test("+++\u07ff", "{}", "\u07ff"); // NKO TAMAN SIGN
+
+  // 3-byte code points
+  sstr.width(5);
+  test("++\u0800", "{}", "\u0800"); // SAMARITAN LETTER ALAF
+  sstr.width(5);
+  test("++\ufffd", "{}", "\ufffd"); // REPLACEMENT CHARACTER
+
+  // 4-byte code points
+  sstr.width(5);
+  test("+\U00010000", "{}", "\U00010000"); // LINEAR B SYLLABLE B008 A
+  sstr.width(5);
+  test("+\U0010FFFF", "{}", "\U0010FFFF"); // Undefined Character
+}
+
+int main(int, char**) {
+  print_tests(test_file, test_exception);
+
+  test_sentry_failure();
+  test_format_exception();
+  test_write_failure();
+  test_stream_formatting();
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print_tests.h b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print_tests.h
new file mode 100644
index 00000000000000..d28256cabc7986
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print_tests.h
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_PRINT_FUN_PRINT_TESTS_H
+#define TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_PRINT_FUN_PRINT_TESTS_H
+
+template <class TestFunction, class ExceptionTest>
+void print_tests(TestFunction check, ExceptionTest check_exception) {
+  // *** Test escaping  ***
+
+  check("{", "{{");
+  check("{:^}", "{{:^}}");
+  check("{: ^}", "{{:{}^}}", ' ');
+  check("{:{}^}", "{{:{{}}^}}");
+  check("{:{ }^}", "{{:{{{}}}^}}", ' ');
+
+  // *** Test argument ID ***
+  check("hello false true", "hello {0:} {1:}", false, true);
+  check("hello true false", "hello {1:} {0:}", false, true);
+
+  // *** Test many arguments ***
+  check(
+      "1234567890\t1234567890",
+      "{}{}{}{}{}{}{}{}{}{}\t{}{}{}{}{}{}{}{}{}{}",
+      1,
+      2,
+      3,
+      4,
+      5,
+      6,
+      7,
+      8,
+      9,
+      0,
+      1,
+      2,
+      3,
+      4,
+      5,
+      6,
+      7,
+      8,
+      9,
+      0);
+
+  // *** Test embedded NUL character ***
+  using namespace std::literals;
+  check("hello\0world"sv, "hello{}{}", '\0', "world");
+  check("hello\0world"sv, "hello\0{}"sv, "world");
+  check("hello\0world"sv, "hello{}", "\0world"sv);
+
+  // *** Test Unicode ***
+  // 2-byte code points
+  check("\u00a1"sv, "{}"sv, "\u00a1");  // INVERTED EXCLAMATION MARK
+  check("\u07ff"sv, "{:}"sv, "\u07ff"); // NKO TAMAN SIGN
+
+  // 3-byte code points
+  check("\u0800"sv, "{}"sv, "\u0800"); // SAMARITAN LETTER ALAF
+  check("\ufffd"sv, "{}"sv, "\ufffd"); // REPLACEMENT CHARACTER
+
+  // 4-byte code points
+  check("\U00010000"sv, "{}"sv, "\U00010000"); // LINEAR B SYLLABLE B008 A
+  check("\U0010FFFF"sv, "{}"sv, "\U0010FFFF"); // Undefined Character
+
+  // *** Test invalid format strings ***
+  check_exception("The format string terminates at a '{'", "{");
+  check_exception("The replacement field misses a terminating '}'", "{:", 42);
+
+  check_exception("The format string contains an invalid escape sequence", "}");
+  check_exception("The format string contains an invalid escape sequence", "{:}-}", 42);
+
+  check_exception("The format string contains an invalid escape sequence", "} ");
+  check_exception("The argument index starts with an invalid character", "{-", 42);
+  check_exception("The argument index value is too large for the number of arguments supplied", "hello {}");
+  check_exception("The argument index value is too large for the number of arguments supplied", "hello {0}");
+  check_exception("The argument index value is too large for the number of arguments supplied", "hello {1}", 42);
+}
+
+#endif // TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_PRINT_FUN_PRINT_TESTS_H
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/println.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/println.pass.cpp
new file mode 100644
index 00000000000000..deb262d2fb627e
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/println.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO PRINT Investigate see https://reviews.llvm.org/D156585
+// UNSUPPORTED: no-filesystem
+
+// XFAIL: availability-fp_to_chars-missing
+// XFAIL: availability-print-missing
+
+// <ostream>
+
+// template<class... Args>
+//   void println(ostream& os, format_string<Args...> fmt, Args&&... args);
+
+// [ostream.formatted.print]/3
+//   If the function is vprint_unicode and os is a stream that refers to
+//   a terminal capable of displaying Unicode which is determined in an
+//   implementation-defined manner, writes out to the terminal using the
+//   native Unicode API;
+// This is tested in
+// test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
+
+#include <cassert>
+#include <ostream>
+#include <sstream>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "print_tests.h"
+#include "test_format_string.h"
+#include "test_macros.h"
+
+auto test_file = []<class... Args>(std::string_view e, test_format_string<char, Args...> fmt, Args&&... args) {
+  std::string expected = std::string{e} + '\n';
+
+  std::stringstream sstr;
+  std::println(sstr, fmt, std::forward<Args>(args)...);
+
+  std::string out = sstr.str();
+  TEST_REQUIRE(out == expected,
+               TEST_WRITE_CONCATENATED(
+                   "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+};
+
+auto test_exception = []<class... Args>(std::string_view, std::string_view, Args&&...) {
+  // After P2216 most exceptions thrown by std::format become ill-formed.
+  // Therefore this tests does nothing.
+  // A basic ill-formed test is done in format.verify.cpp
+  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
+};
+
+int main(int, char**) {
+  print_tests(test_file, test_exception);
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp
new file mode 100644
index 00000000000000..671df8c065b401
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp
@@ -0,0 +1,198 @@
+//===----------------------------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO PRINT Investigate see https://reviews.llvm.org/D156585
+// UNSUPPORTED: no-filesystem
+
+// XFAIL: availability-fp_to_chars-missing
+// XFAIL: availability-print-missing
+
+// <ostream>
+
+// void vprint_nonunicode(ostream& os, string_view fmt, format_args args);
+
+// [ostream.formatted.print]/3
+//   If the function is vprint_unicode and os is a stream that refers to
+//   a terminal capable of displaying Unicode which is determined in an
+//   implementation-defined manner, writes out to the terminal using the
+//   native Unicode API;
+// This is tested in
+// test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
+
+#include <cassert>
+#include <ostream>
+#include <sstream>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "print_tests.h"
+#include "test_format_string.h"
+#include "test_macros.h"
+
+auto test_file = []<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
+  std::stringstream sstr;
+  std::vprint_nonunicode(sstr, fmt.get(), std::make_format_args(args...));
+
+  std::string out = sstr.str();
+  TEST_REQUIRE(out == expected,
+               TEST_WRITE_CONCATENATED(
+                   "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+};
+
+auto test_exception = []<class... Args>(std::string_view, std::string_view, Args&&...) {
+  // After P2216 most exceptions thrown by std::format become ill-formed.
+  // Therefore this tests does nothing.
+  // A basic ill-formed test is done in format.verify.cpp
+  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
+};
+// [ostream.formatted.print]/3.2
+//   ...
+//   After constructing a sentry object, the function initializes an automatic variable via
+//     string out = vformat(os.getloc(), fmt, args);
+// This means if both
+// - creating a sentry fails
+// - the formatting fails
+// the first one "wins" and the format_error is not thrown.
+static void test_sentry_failure() {
+  // In order for the creation of a sentry to fail a tied stream's
+  // sync operation should fail.
+  struct sync_failure : public std::basic_streambuf<char> {
+  protected:
+    int virtual sync() { return -1; }
+  };
+  sync_failure buf_tied;
+  std::ostream os_tied(&buf_tied);
+  os_tied.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
+
+  std::stringstream os;
+  os.tie(&os_tied);
+  os.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
+
+  TEST_THROWS_TYPE(std::ios_base::failure, std::vprint_nonunicode(os, "valid", std::make_format_args()));
+  os_tied.clear();
+  [[maybe_unused]] int arg = -10;
+  TEST_THROWS_TYPE(std::ios_base::failure,
+                   std::vprint_nonunicode(os, "throws exception at run-time {0:{0}}", std::make_format_args(arg)));
+
+  os.exceptions(std::stringstream::goodbit);
+  os.setstate(std::stringstream::failbit);
+  std::vprint_nonunicode(
+      os, "not called when the os.good() is false, so no exception is thrown {0:{0}}", std::make_format_args(arg));
+}
+
+// [ostream.formatted.print]/3.2
+//   any exception thrown by the call to vformat is propagated without
+//   regard to the value of os.exceptions() and without turning on
+//   ios_base​::​badbit in the error state of os.
+// Most invalid format strings are checked at compile-time. An invalid
+// value for the width can only be tested run-time.
+static void test_format_exception() {
+  std::stringstream sstr;
+  assert(sstr.good());
+
+  [[maybe_unused]] int arg = -10;
+  TEST_THROWS_TYPE(std::format_error, std::vprint_nonunicode(sstr, "no output {0:{0}}", std::make_format_args(arg)));
+  assert(sstr.good());
+  assert(sstr.str().empty());
+
+  sstr.exceptions(std::stringstream::goodbit);
+  TEST_THROWS_TYPE(std::format_error, std::vprint_nonunicode(sstr, "no output {0:{0}}", std::make_format_args(arg)));
+  assert(sstr.good());
+  assert(sstr.str().empty());
+
+  sstr.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
+  TEST_THROWS_TYPE(std::format_error, std::vprint_nonunicode(sstr, "no output {0:{0}}", std::make_format_args(arg)));
+  assert(sstr.good());
+  assert(sstr.str().empty());
+}
+
+static void test_write_failure() {
+  // Stream that fails to write a single character.
+  struct overflow_failure : public std::basic_streambuf<char> {
+  protected:
+    int virtual overflow(int) { return std::char_traits<char>::eof(); }
+  };
+  overflow_failure buf;
+  std::ostream os(&buf);
+  [[maybe_unused]] int arg = -10;
+  os.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
+
+  TEST_THROWS_TYPE(std::ios_base::failure, std::vprint_nonunicode(os, "valid", std::make_format_args()));
+  os.clear();
+  // When the parser would directly write to the output instead of
+  // formatting first it would fail writing the first character 't' of
+  // the string and result in a std::ios_base::failure exception.
+  TEST_THROWS_TYPE(std::format_error,
+                   std::vprint_nonunicode(os, "throws exception at run-time {0:{0}}", std::make_format_args(arg)));
+
+  os.exceptions(std::stringstream::goodbit);
+  os.clear();
+  std::vprint_nonunicode(os, "valid", std::make_format_args());
+  assert(os.fail());
+}
+
+static void test_stream_formatting() {
+  std::stringstream sstr;
+  auto test = [&]<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
+    sstr.str("");
+    std::vprint_nonunicode(sstr, fmt.get(), std::make_format_args(args...));
+
+    std::string out = sstr.str();
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  };
+
+  test("hello", "{}", "hello");
+
+  sstr.width(10);
+  test("     hello", "{}", "hello");
+
+  sstr.fill('+');
+
+  sstr.width(10);
+  test("+++++hello", "{}", "hello");
+
+  // *** Test embedded NUL character ***
+  using namespace std::literals;
+  sstr.width(15);
+  test("++++hello\0world"sv, "hello{}{}", '\0', "world");
+
+  // *** Test Unicode ***
+  // Streams count code units not code points
+  // 2-byte code points
+  sstr.width(5);
+  test("+++\u00a1", "{}", "\u00a1"); // INVERTED EXCLAMATION MARK
+  sstr.width(5);
+  test("+++\u07ff", "{}", "\u07ff"); // NKO TAMAN SIGN
+
+  // 3-byte code points
+  sstr.width(5);
+  test("++\u0800", "{}", "\u0800"); // SAMARITAN LETTER ALAF
+  sstr.width(5);
+  test("++\ufffd", "{}", "\ufffd"); // REPLACEMENT CHARACTER
+
+  // 4-byte code points
+  sstr.width(5);
+  test("+\U00010000", "{}", "\U00010000"); // LINEAR B SYLLABLE B008 A
+  sstr.width(5);
+  test("+\U0010FFFF", "{}", "\U0010FFFF"); // Undefined Character
+}
+
+int main(int, char**) {
+  print_tests(test_file, test_exception);
+
+  test_sentry_failure();
+  test_format_exception();
+  test_write_failure();
+  test_stream_formatting();
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
new file mode 100644
index 00000000000000..fb6d6894884099
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
@@ -0,0 +1,197 @@
+//===----------------------------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO PRINT Investigate see https://reviews.llvm.org/D156585
+// UNSUPPORTED: no-filesystem
+
+// XFAIL: availability-fp_to_chars-missing
+// XFAIL: availability-print-missing
+
+// <ostream>
+
+// void vprint_unicode(ostream& os, string_view fmt, format_args args);
+// [ostream.formatted.print]/3
+//   If the function is vprint_unicode and os is a stream that refers to
+//   a terminal capable of displaying Unicode which is determined in an
+//   implementation-defined manner, writes out to the terminal using the
+//   native Unicode API;
+// This is tested in
+// test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
+
+#include <cassert>
+#include <ostream>
+#include <sstream>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "print_tests.h"
+#include "test_format_string.h"
+#include "test_macros.h"
+
+auto test_file = []<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
+  std::stringstream sstr;
+  std::vprint_unicode(sstr, fmt.get(), std::make_format_args(args...));
+
+  std::string out = sstr.str();
+  TEST_REQUIRE(out == expected,
+               TEST_WRITE_CONCATENATED(
+                   "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+};
+
+auto test_exception = []<class... Args>(std::string_view, std::string_view, Args&&...) {
+  // After P2216 most exceptions thrown by std::format become ill-formed.
+  // Therefore this tests does nothing.
+  // A basic ill-formed test is done in format.verify.cpp
+  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
+};
+// [ostream.formatted.print]/3.2
+//   ...
+//   After constructing a sentry object, the function initializes an automatic variable via
+//     string out = vformat(os.getloc(), fmt, args);
+// This means if both
+// - creating a sentry fails
+// - the formatting fails
+// the first one "wins" and the format_error is not thrown.
+static void test_sentry_failure() {
+  // In order for the creation of a sentry to fail a tied stream's
+  // sync operation should fail.
+  struct sync_failure : public std::basic_streambuf<char> {
+  protected:
+    int virtual sync() { return -1; }
+  };
+  sync_failure buf_tied;
+  std::ostream os_tied(&buf_tied);
+  os_tied.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
+
+  std::stringstream os;
+  os.tie(&os_tied);
+  os.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
+
+  [[maybe_unused]] int arg = -10;
+  TEST_THROWS_TYPE(std::ios_base::failure, std::vprint_unicode(os, "valid", std::make_format_args()));
+  os_tied.clear();
+  TEST_THROWS_TYPE(std::ios_base::failure,
+                   std::vprint_unicode(os, "throws exception at run-time {0:{0}}", std::make_format_args(arg)));
+
+  os.exceptions(std::stringstream::goodbit);
+  os.setstate(std::stringstream::failbit);
+  std::vprint_unicode(
+      os, "not called when the os.good() is false, so no exception is thrown {0:{0}}", std::make_format_args(arg));
+}
+
+// [ostream.formatted.print]/3.2
+//   any exception thrown by the call to vformat is propagated without
+//   regard to the value of os.exceptions() and without turning on
+//   ios_base::badbit in the error state of os.
+// Most invalid format strings are checked at compile-time. An invalid
+// value for the width can only be tested run-time.
+static void test_format_exception() {
+  std::stringstream sstr;
+  assert(sstr.good());
+
+  [[maybe_unused]] int arg = -10;
+  TEST_THROWS_TYPE(std::format_error, std::vprint_unicode(sstr, "no output {0:{0}}", std::make_format_args(arg)));
+  assert(sstr.good());
+  assert(sstr.str().empty());
+
+  sstr.exceptions(std::stringstream::goodbit);
+  TEST_THROWS_TYPE(std::format_error, std::vprint_unicode(sstr, "no output {0:{0}}", std::make_format_args(arg)));
+  assert(sstr.good());
+  assert(sstr.str().empty());
+
+  sstr.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
+  TEST_THROWS_TYPE(std::format_error, std::vprint_unicode(sstr, "no output {0:{0}}", std::make_format_args(arg)));
+  assert(sstr.good());
+  assert(sstr.str().empty());
+}
+
+static void test_write_failure() {
+  // Stream that fails to write a single character.
+  struct overflow_failure : public std::basic_streambuf<char> {
+  protected:
+    int virtual overflow(int) { return std::char_traits<char>::eof(); }
+  };
+  overflow_failure buf;
+  std::ostream os(&buf);
+  os.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
+
+  TEST_THROWS_TYPE(std::ios_base::failure, std::vprint_unicode(os, "valid", std::make_format_args()));
+  os.clear();
+  // When the parser would directly write to the output instead of
+  // formatting first it would fail writing the first character 't' of
+  // the string and result in a std::ios_base::failure exception.
+  [[maybe_unused]] int arg = -10;
+  TEST_THROWS_TYPE(
+      std::format_error, std::vprint_unicode(os, "throws exception at run-time {0:{0}}", std::make_format_args(arg)));
+
+  os.exceptions(std::stringstream::goodbit);
+  os.clear();
+  std::vprint_unicode(os, "valid", std::make_format_args());
+  assert(os.fail());
+}
+
+static void test_stream_formatting() {
+  std::stringstream sstr;
+  auto test = [&]<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
+    sstr.str("");
+    std::vprint_unicode(sstr, fmt.get(), std::make_format_args(args...));
+
+    std::string out = sstr.str();
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  };
+
+  test("hello", "{}", "hello");
+
+  sstr.width(10);
+  test("     hello", "{}", "hello");
+
+  sstr.fill('+');
+
+  sstr.width(10);
+  test("+++++hello", "{}", "hello");
+
+  // *** Test embedded NUL character ***
+  using namespace std::literals;
+  sstr.width(15);
+  test("++++hello\0world"sv, "hello{}{}", '\0', "world");
+
+  // *** Test Unicode ***
+  // Streams count code units not code points
+  // 2-byte code points
+  sstr.width(5);
+  test("+++\u00a1", "{}", "\u00a1"); // INVERTED EXCLAMATION MARK
+  sstr.width(5);
+  test("+++\u07ff", "{}", "\u07ff"); // NKO TAMAN SIGN
+
+  // 3-byte code points
+  sstr.width(5);
+  test("++\u0800", "{}", "\u0800"); // SAMARITAN LETTER ALAF
+  sstr.width(5);
+  test("++\ufffd", "{}", "\ufffd"); // REPLACEMENT CHARACTER
+
+  // 4-byte code points
+  sstr.width(5);
+  test("+\U00010000", "{}", "\U00010000"); // LINEAR B SYLLABLE B008 A
+  sstr.width(5);
+  test("+\U0010FFFF", "{}", "\U0010FFFF"); // Undefined Character
+}
+
+int main(int, char**) {
+  print_tests(test_file, test_exception);
+
+  test_sentry_failure();
+  test_format_exception();
+  test_write_failure();
+  test_stream_formatting();
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp
index 720322081e1ef2..2a42ca080612de 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp
@@ -89,17 +89,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_print
-#     error "__cpp_lib_print should be defined in c++23"
-#   endif
-#   if __cpp_lib_print != 202207L
-#     error "__cpp_lib_print should have the value 202207L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_print
-#     error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_print
+#   error "__cpp_lib_print should be defined in c++23"
+# endif
+# if __cpp_lib_print != 202207L
+#   error "__cpp_lib_print should have the value 202207L in c++23"
 # endif
 
 #elif TEST_STD_VER > 23
@@ -117,17 +111,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_print
-#     error "__cpp_lib_print should be defined in c++26"
-#   endif
-#   if __cpp_lib_print != 202207L
-#     error "__cpp_lib_print should have the value 202207L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_print
-#     error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_print
+#   error "__cpp_lib_print should be defined in c++26"
+# endif
+# if __cpp_lib_print != 202207L
+#   error "__cpp_lib_print should have the value 202207L in c++26"
 # endif
 
 #endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp
index 5284848ead8418..f4ccea4e86304c 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp
@@ -50,32 +50,20 @@
 
 #elif TEST_STD_VER == 23
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_print
-#     error "__cpp_lib_print should be defined in c++23"
-#   endif
-#   if __cpp_lib_print != 202207L
-#     error "__cpp_lib_print should have the value 202207L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_print
-#     error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_print
+#   error "__cpp_lib_print should be defined in c++23"
+# endif
+# if __cpp_lib_print != 202207L
+#   error "__cpp_lib_print should have the value 202207L in c++23"
 # endif
 
 #elif TEST_STD_VER > 23
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_print
-#     error "__cpp_lib_print should be defined in c++26"
-#   endif
-#   if __cpp_lib_print != 202207L
-#     error "__cpp_lib_print should have the value 202207L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_print
-#     error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_print
+#   error "__cpp_lib_print should be defined in c++26"
+# endif
+# if __cpp_lib_print != 202207L
+#   error "__cpp_lib_print should have the value 202207L in c++26"
 # endif
 
 #endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index c0d3d554dcf056..650a14b019ed87 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -5243,17 +5243,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_print
-#     error "__cpp_lib_print should be defined in c++23"
-#   endif
-#   if __cpp_lib_print != 202207L
-#     error "__cpp_lib_print should have the value 202207L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_print
-#     error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_print
+#   error "__cpp_lib_print should be defined in c++23"
+# endif
+# if __cpp_lib_print != 202207L
+#   error "__cpp_lib_print should have the value 202207L in c++23"
 # endif
 
 # ifndef __cpp_lib_quoted_string_io
@@ -6950,17 +6944,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_print
-#     error "__cpp_lib_print should be defined in c++26"
-#   endif
-#   if __cpp_lib_print != 202207L
-#     error "__cpp_lib_print should have the value 202207L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_print
-#     error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_print
+#   error "__cpp_lib_print should be defined in c++26"
+# endif
+# if __cpp_lib_print != 202207L
+#   error "__cpp_lib_print should have the value 202207L in c++26"
 # endif
 
 # ifndef __cpp_lib_quoted_string_io
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 6a30324397883a..ec7d8ff9f707e6 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -873,7 +873,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_print",
             "values": {"c++23": 202207},
             "headers": ["ostream", "print"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_quoted_string_io",
diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py
index 5e854917e6ef45..d3c5dedf4ea286 100644
--- a/libcxx/utils/libcxx/test/features.py
+++ b/libcxx/utils/libcxx/test/features.py
@@ -570,6 +570,7 @@ def check_gdb(cfg):
             cfg.available_features,
         ),
     ),
+
     # Tests that require time zone database support in the built library
     Feature(
         name="availability-tzdb-missing",
@@ -579,4 +580,14 @@ def check_gdb(cfg):
             cfg.available_features,
         ),
     ),
+
+    # Tests that require __libcpp_print support in the built library
+    Feature(
+        name="availability-print-missing",
+        when=lambda cfg: BooleanExpression.evaluate(
+            # TODO(ldionne) Please provide the correct value.
+            "stdlib=apple-libc++ && target={{.+}}-apple-macosx{{(10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0|13.0)(.0)?}}",
+            cfg.available_features,
+        ),
+    ),
 ]

>From f2aa35db131bb658b8beb9b3507aae891c55e68b Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sat, 16 Dec 2023 13:37:41 +0100
Subject: [PATCH 2/2] Addresses review comments.

---
 libcxx/include/fstream                        |  4 +--
 libcxx/include/ostream                        | 15 +++++++++--
 libcxx/include/print                          | 10 +++----
 libcxx/src/ostream.cpp                        | 14 +++++-----
 libcxx/src/std_stream.h                       |  4 +--
 .../vprint_unicode.pass.cpp                   | 26 +++++++++----------
 .../ostream.formatted.print/print_tests.h     |  6 ++---
 .../vprint_nonunicode.pass.cpp                | 21 +++++++++++----
 .../vprint_unicode.pass.cpp                   | 21 +++++++++++----
 libcxx/utils/libcxx/test/features.py          |  2 +-
 10 files changed, 78 insertions(+), 45 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 812225d549eab6..c5a589698e2265 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -256,8 +256,6 @@ public:
     inline static const char*
     __make_mdstring(ios_base::openmode __mode) _NOEXCEPT;
 
-    _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI FILE* __file() { return __file_; }
-
   protected:
     // 27.9.1.5 Overridden virtual functions:
     int_type underflow() override;
@@ -291,6 +289,8 @@ private:
 
   bool __read_mode();
   void __write_mode();
+
+  _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 };
 
 template <class _CharT, class _Traits>
diff --git a/libcxx/include/ostream b/libcxx/include/ostream
index 5c5c30de006857..4f51d088a05bb7 100644
--- a/libcxx/include/ostream
+++ b/libcxx/include/ostream
@@ -1214,9 +1214,20 @@ extern template class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS basic_ostream<wchar_t>;
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void
 __vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
+  // [ostream.formatted.print]/3
+  // Effects: Behaves as a formatted output function
+  // ([ostream.formatted.reqmts]) of os, except that:
+  // - failure to generate output is reported as specified below, and
+  // - any exception thrown by the call to vformat is propagated without regard
+  //   to the value of os.exceptions() and without turning on ios_base::badbit
+  //   in the error state of os.
+  // After constructing a sentry object, the function initializes an automatic
+  // variable via
+  //   string out = vformat(os.getloc(), fmt, args);
+
   ostream::sentry __s(__os);
   if (__s) {
-    string __o = vformat(__os.getloc(), __fmt, __args);
+    string __o = std::vformat(__os.getloc(), __fmt, __args);
     if (__write_nl)
       __o += '\n';
 
@@ -1289,7 +1300,7 @@ __vprint_unicode(ostream& __os, string_view __fmt, format_args __args, bool __wr
 #    endif // _LIBCPP_HAS_NO_EXCEPTIONS
     ostream::sentry __s(__os);
     if (__s) {
-#    ifndef _WIN32
+#    ifndef _LIBCPP_WIN32API
       __print::__vprint_unicode_posix(__file, __fmt, __args, __write_nl, true);
 #    elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
     __print::__vprint_unicode_windows(__file, __fmt, __args, __write_nl, true);
diff --git a/libcxx/include/print b/libcxx/include/print
index 2174557794e4df..e076b6e4864f8c 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -59,7 +59,7 @@ namespace std {
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#ifdef _WIN32
+#ifdef __LIBCPP_WIN32API
 _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
 
 #  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
@@ -75,7 +75,7 @@ _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
 _LIBCPP_EXPORTED_FROM_ABI void __write_to_windows_console(FILE* __stream, wstring_view __view);
 #  endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
 
-#endif // _WIN32
+#endif // __LIBCPP_WIN32API
 
 #if _LIBCPP_STD_VER >= 23
 
@@ -202,7 +202,7 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal(FILE* __stream) {
   // the behavior in the test. This is not part of the public API.
 #  ifdef _LIBCPP_TESTING_PRINT_IS_TERMINAL
   return _LIBCPP_TESTING_PRINT_IS_TERMINAL(__stream);
-#  elif defined(_WIN32)
+#  elif defined(__LIBCPP_WIN32API)
   return std::__is_windows_terminal(__stream);
 #  elif __has_include(<unistd.h>)
   return isatty(fileno(__stream));
@@ -275,7 +275,7 @@ __vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args,
   // the behavior in the test. This is not part of the public API.
 #      ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
   _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION(__stream, __view);
-#      elif defined(_WIN32)
+#      elif defined(__LIBCPP_WIN32API)
   std::__write_to_windows_console(__stream, __view);
 #      else
   std::__throw_runtime_error("No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
@@ -313,7 +313,7 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
   // so there the call can be forwarded to the non_unicode API. On
   // Windows there is a different API. This API requires transcoding.
 
-#    ifndef _WIN32
+#    ifndef __LIBCPP_WIN32API
   __print::__vprint_unicode_posix(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
 #    elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
   __print::__vprint_unicode_windows(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
diff --git a/libcxx/src/ostream.cpp b/libcxx/src/ostream.cpp
index b91cb57d607caa..bba8e6550710f8 100644
--- a/libcxx/src/ostream.cpp
+++ b/libcxx/src/ostream.cpp
@@ -19,21 +19,21 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 _LIBCPP_AVAILABILITY_PRINT _LIBCPP_EXPORTED_FROM_ABI FILE* __get_ostream_file(ostream& __os) {
   // dynamic_cast requires RTTI, this only affects users whose vendor builds
-  // the dylib with RTTI disabled. It does not affect users why build with RTTI
-  // disabled with a dylib with RTTI enabled.
+  // the dylib with RTTI disabled. It does not affect users who build with RTTI
+  // disabled but use a dylib where the RTTI is enabled.
   //
-  // Not returning a FILE* means the stream is not considered a terminal and
-  // the special terminal handling is not done. The terminal handling is mainly
-  // of importance on Windows.
+  // Returning a nullptr means the stream is not considered a terminal and the
+  // special terminal handling is not done. The terminal handling is mainly of
+  // importance on Windows.
 #ifndef _LIBCPP_HAS_NO_RTTI
   auto* __rdbuf = __os.rdbuf();
 #  ifndef _LIBCPP_HAS_NO_FILESYSTEM
   if (auto* __buffer = dynamic_cast<filebuf*>(__rdbuf))
-    return __buffer->__file();
+    return __buffer->__file_;
 #  endif
 
   if (auto* __buffer = dynamic_cast<__stdoutbuf<char>*>(__rdbuf))
-    return __buffer->__file();
+    return __buffer->__file_;
 #endif // _LIBCPP_HAS_NO_RTTI
 
   return nullptr;
diff --git a/libcxx/src/std_stream.h b/libcxx/src/std_stream.h
index 21388e19a9656a..0f6ff4460a53e2 100644
--- a/libcxx/src/std_stream.h
+++ b/libcxx/src/std_stream.h
@@ -286,8 +286,6 @@ class _LIBCPP_HIDDEN __stdoutbuf
 
     __stdoutbuf(FILE* __fp, state_type* __st);
 
-    [[nodiscard]] FILE* __file() { return __file_; }
-
   protected:
     virtual int_type overflow (int_type __c = traits_type::eof());
     virtual streamsize xsputn(const char_type* __s, streamsize __n);
@@ -308,6 +306,8 @@ class _LIBCPP_HIDDEN __stdoutbuf
 
     __stdoutbuf(const __stdoutbuf&);
     __stdoutbuf& operator=(const __stdoutbuf&);
+
+    _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 };
 
 template <class _CharT>
diff --git a/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp b/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
index 6243b250dec393..7b590f1e80c4b5 100644
--- a/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
+++ b/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
@@ -20,10 +20,9 @@
 //  void __vprint_unicode(ostream& os, string_view fmt,
 //                        format_args args, bool write_nl);
 
-// In the library when the std::cout is redirected to a file it is no
-// longer considered a terminal and the special terminal handling is no
-// longer executed. By testing this function we can "force" emulate a
-// terminal.
+// In the library when std::cout is redirected to a file it is no longer
+// considered a terminal and the special terminal handling is no longer
+// executed. By testing this function we can "force" emulate a terminal.
 // Note write_nl is tested by the public API.
 
 #include <cstdio>
@@ -39,23 +38,21 @@ bool is_terminal(FILE*);
 
 #include "test_macros.h"
 
-#include <print> // TODO REMOVE
-
 scoped_test_env env;
 std::string filename = env.create_file("output.txt");
 
 int is_terminal_calls        = 0;
-bool should_call_is_terminal = false;
 bool is_terminal_result      = false;
 bool is_terminal(FILE*) {
   ++is_terminal_calls;
-  assert(should_call_is_terminal);
   return is_terminal_result;
 }
 
+// When the stream is not a file stream, cout, clog, or cerr the stream does not
+// considered to be backed by a FILE*. Then the stream should never check
+// whether it's a terminal.
 static void test_is_terminal_not_a_file_stream() {
   is_terminal_calls       = 0;
-  should_call_is_terminal = false;
   is_terminal_result      = false;
   {
     std::stringstream stream;
@@ -68,9 +65,10 @@ static void test_is_terminal_not_a_file_stream() {
   assert(is_terminal_calls == 0);
 }
 
+// When the stream is a file stream, its FILE* may be a terminal. Validate this
+// is tested.
 static void test_is_terminal_file_stream() {
   is_terminal_calls       = 0;
-  should_call_is_terminal = true;
   is_terminal_result      = false;
   {
     std::fstream stream(filename);
@@ -88,11 +86,11 @@ static void test_is_terminal_file_stream() {
   }
 }
 
+// The same as above, but this time test for derived classes.
 static void test_is_terminal_rdbuf_derived_from_filebuf() {
   struct my_filebuf : public std::filebuf {};
 
   is_terminal_calls       = 0;
-  should_call_is_terminal = true;
   is_terminal_result      = false;
 
   my_filebuf buf;
@@ -104,9 +102,10 @@ static void test_is_terminal_rdbuf_derived_from_filebuf() {
   assert(is_terminal_calls == 1);
 }
 
+// When the stream is cout, clog, or cerr, its FILE* may be a terminal. Validate
+// this is tested.
 static void test_is_terminal_std_cout_cerr_clog() {
   is_terminal_calls       = 0;
-  should_call_is_terminal = true;
   is_terminal_result      = false;
   {
     std::print(std::cout, "test");
@@ -122,6 +121,8 @@ static void test_is_terminal_std_cout_cerr_clog() {
   }
 }
 
+// When the stream's FILE* is a terminal the contents need to be flushed before
+// writing to the stream.
 static void test_is_terminal_is_flushed() {
   struct sync_counter : public std::filebuf {
     sync_counter() {
@@ -137,7 +138,6 @@ static void test_is_terminal_is_flushed() {
     }
   };
 
-  should_call_is_terminal = true;
   is_terminal_result      = false;
 
   sync_counter buf;
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print_tests.h b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print_tests.h
index d28256cabc7986..f5a6a639cbd307 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print_tests.h
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print_tests.h
@@ -5,8 +5,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_PRINT_FUN_PRINT_TESTS_H
-#define TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_PRINT_FUN_PRINT_TESTS_H
+#ifndef TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_OUTPUT_STREAMS_OSTREAM_FORMATTED_OSTREAM_FORMATTED_PRINT_PRINT_TESTS_H
+#define TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_OUTPUT_STREAMS_OSTREAM_FORMATTED_OSTREAM_FORMATTED_PRINT_PRINT_TESTS_H
 
 template <class TestFunction, class ExceptionTest>
 void print_tests(TestFunction check, ExceptionTest check_exception) {
@@ -80,4 +80,4 @@ void print_tests(TestFunction check, ExceptionTest check_exception) {
   check_exception("The argument index value is too large for the number of arguments supplied", "hello {1}", 42);
 }
 
-#endif // TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_PRINT_FUN_PRINT_TESTS_H
+#endif // TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_OUTPUT_STREAMS_OSTREAM_FORMATTED_OSTREAM_FORMATTED_PRINT_PRINT_TESTS_H
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp
index 671df8c065b401..350e20387b01c3 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp
@@ -46,12 +46,23 @@ auto test_file = []<class... Args>(std::string_view expected, test_format_string
                    "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
 };
 
-auto test_exception = []<class... Args>(std::string_view, std::string_view, Args&&...) {
-  // After P2216 most exceptions thrown by std::format become ill-formed.
-  // Therefore this tests does nothing.
-  // A basic ill-formed test is done in format.verify.cpp
-  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
+auto test_exception = []< class... Args>([[maybe_unused]] std::string_view what,
+                                         [[maybe_unused]] std::string_view fmt,
+                                         [[maybe_unused]] Args&&... args) {
+  TEST_VALIDATE_EXCEPTION(
+      std::format_error,
+      [&]([[maybe_unused]] const std::format_error& e) {
+        TEST_LIBCPP_REQUIRE(
+            e.what() == what,
+            TEST_WRITE_CONCATENATED(
+                "\nFormat string   ", fmt, "\nExpected exception ", what, "\nActual exception   ", e.what(), '\n'));
+      },
+      [&] {
+        std::stringstream sstr;
+        std::vprint_nonunicode(sstr, fmt, std::make_format_args(args...));
+      }());
 };
+
 // [ostream.formatted.print]/3.2
 //   ...
 //   After constructing a sentry object, the function initializes an automatic variable via
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
index fb6d6894884099..9b14c429bdb45f 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
@@ -45,12 +45,23 @@ auto test_file = []<class... Args>(std::string_view expected, test_format_string
                    "\nFormat string   ", fmt.get(), "\nExpected output ", expected, "\nActual output   ", out, '\n'));
 };
 
-auto test_exception = []<class... Args>(std::string_view, std::string_view, Args&&...) {
-  // After P2216 most exceptions thrown by std::format become ill-formed.
-  // Therefore this tests does nothing.
-  // A basic ill-formed test is done in format.verify.cpp
-  // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
+auto test_exception = []< class... Args>([[maybe_unused]] std::string_view what,
+                                         [[maybe_unused]] std::string_view fmt,
+                                         [[maybe_unused]] Args&&... args) {
+  TEST_VALIDATE_EXCEPTION(
+      std::format_error,
+      [&]([[maybe_unused]] const std::format_error& e) {
+        TEST_LIBCPP_REQUIRE(
+            e.what() == what,
+            TEST_WRITE_CONCATENATED(
+                "\nFormat string   ", fmt, "\nExpected exception ", what, "\nActual exception   ", e.what(), '\n'));
+      },
+      [&] {
+        std::stringstream sstr;
+        std::vprint_unicode(sstr, fmt, std::make_format_args(args...));
+      }());
 };
+
 // [ostream.formatted.print]/3.2
 //   ...
 //   After constructing a sentry object, the function initializes an automatic variable via
diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py
index d3c5dedf4ea286..7ec06389f7d9f6 100644
--- a/libcxx/utils/libcxx/test/features.py
+++ b/libcxx/utils/libcxx/test/features.py
@@ -581,7 +581,7 @@ def check_gdb(cfg):
         ),
     ),
 
-    # Tests that require __libcpp_print support in the built library
+    # Tests that require support for <print> and std::print in <ostream> in the built library.
     Feature(
         name="availability-print-missing",
         when=lambda cfg: BooleanExpression.evaluate(



More information about the libcxx-commits mailing list