public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [committed] libstdc++: Add std::formatter specializations for extended float types
@ 2023-08-17 20:32 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2023-08-17 20:32 UTC (permalink / raw)
  To: libstdc++, gcc-patches

Tested x86_64-linux. Pushed to trunk.

-- >8 --

This makes it possible to format _Float32, _Float64 etc. in C++20 mode.
Previously it was only possible to format them in C++23 when the
<stdfloat> typedefs and the std::to_chars overloads were defined.

Instead of relying on std::to_chars for those types, we can just reuse
the formatters for float, double and long double. This also avoids
template bloat by reusing the same specializations instead of
instantiating __formatter_fp for every different type.

libstdc++-v3/ChangeLog:

	* include/std/format (formatter): Add partial specializations
	for extended floating-point types.
	* testsuite/std/format/functions/format.cc: Move test_float128()
	to ...
	* testsuite/std/format/formatter/ext_float.cc: New test.
---
 libstdc++-v3/include/std/format               | 146 +++++++++++++++++-
 .../std/format/formatter/ext_float.cc         |  92 +++++++++++
 .../testsuite/std/format/functions/format.cc  |  33 ----
 3 files changed, 236 insertions(+), 35 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/std/format/formatter/ext_float.cc

diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index 79f810acce3..13f700a10bf 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -2033,6 +2033,7 @@ namespace __format
     };
 #endif
 
+#if defined __cpp_lib_to_chars
   /// Format a floating-point value.
   template<__format::__formattable_float _Tp, __format::__char _CharT>
     struct formatter<_Tp, _CharT>
@@ -2053,6 +2054,140 @@ namespace __format
       __format::__formatter_fp<_CharT> _M_f;
     };
 
+#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+  // Reuse __formatter_fp<C>::format<double, Out> for long double.
+  template<__format::__char _CharT>
+    struct formatter<long double, _CharT>
+    {
+      formatter() = default;
+
+      [[__gnu__::__always_inline__]]
+      constexpr typename basic_format_parse_context<_CharT>::iterator
+      parse(basic_format_parse_context<_CharT>& __pc)
+      { return _M_f.parse(__pc); }
+
+      template<typename _Out>
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(long double __u, basic_format_context<_Out, _CharT>& __fc) const
+	{ return _M_f.format((double)__u, __fc); }
+
+    private:
+      __format::__formatter_fp<_CharT> _M_f;
+    };
+#endif
+
+#if defined(__FLT16_DIG__)
+  // Reuse __formatter_fp<C>::format<float, Out> for _Float16.
+  template<__format::__char _CharT>
+    struct formatter<_Float16, _CharT>
+    {
+      formatter() = default;
+
+      [[__gnu__::__always_inline__]]
+      constexpr typename basic_format_parse_context<_CharT>::iterator
+      parse(basic_format_parse_context<_CharT>& __pc)
+      { return _M_f.parse(__pc); }
+
+      template<typename _Out>
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(_Float16 __u, basic_format_context<_Out, _CharT>& __fc) const
+	{ return _M_f.format((float)__u, __fc); }
+
+    private:
+      __format::__formatter_fp<_CharT> _M_f;
+    };
+#endif
+
+#if defined(__FLT32_DIG__)
+  // Reuse __formatter_fp<C>::format<float, Out> for _Float32.
+  template<__format::__char _CharT>
+    struct formatter<_Float32, _CharT>
+    {
+      formatter() = default;
+
+      [[__gnu__::__always_inline__]]
+      constexpr typename basic_format_parse_context<_CharT>::iterator
+      parse(basic_format_parse_context<_CharT>& __pc)
+      { return _M_f.parse(__pc); }
+
+      template<typename _Out>
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(_Float32 __u, basic_format_context<_Out, _CharT>& __fc) const
+	{ return _M_f.format((float)__u, __fc); }
+
+    private:
+      __format::__formatter_fp<_CharT> _M_f;
+    };
+#endif
+
+#if defined(__FLT64_DIG__)
+  // Reuse __formatter_fp<C>::format<double, Out> for _Float64.
+  template<__format::__char _CharT>
+    struct formatter<_Float64, _CharT>
+    {
+      formatter() = default;
+
+      [[__gnu__::__always_inline__]]
+      constexpr typename basic_format_parse_context<_CharT>::iterator
+      parse(basic_format_parse_context<_CharT>& __pc)
+      { return _M_f.parse(__pc); }
+
+      template<typename _Out>
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(_Float64 __u, basic_format_context<_Out, _CharT>& __fc) const
+	{ return _M_f.format((double)__u, __fc); }
+
+    private:
+      __format::__formatter_fp<_CharT> _M_f;
+    };
+#endif
+
+#if defined(__FLT128_DIG__) && _GLIBCXX_FORMAT_F128 == 1
+  // Reuse __formatter_fp<C>::format<__float128_t, Out> for _Float128.
+  template<__format::__char _CharT>
+    struct formatter<_Float128, _CharT>
+    {
+      formatter() = default;
+
+      [[__gnu__::__always_inline__]]
+      constexpr typename basic_format_parse_context<_CharT>::iterator
+      parse(basic_format_parse_context<_CharT>& __pc)
+      { return _M_f.parse(__pc); }
+
+      template<typename _Out>
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(_Float128 __u, basic_format_context<_Out, _CharT>& __fc) const
+	{ return _M_f.format((__format::__float128_t)__u, __fc); }
+
+    private:
+      __format::__formatter_fp<_CharT> _M_f;
+    };
+#endif
+
+#if defined(__BFLT16_DIG__)
+  // Reuse __formatter_fp<C>::format<float, Out> for bfloat16_t.
+  template<__format::__char _CharT>
+    struct formatter<__gnu_cxx::__bfloat16_t, _CharT>
+    {
+      formatter() = default;
+
+      [[__gnu__::__always_inline__]]
+      constexpr typename basic_format_parse_context<_CharT>::iterator
+      parse(basic_format_parse_context<_CharT>& __pc)
+      { return _M_f.parse(__pc); }
+
+      template<typename _Out>
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(__gnu_cxx::__bfloat16_t __u,
+	       basic_format_context<_Out, _CharT>& __fc) const
+	{ return _M_f.format((float)__u, __fc); }
+
+    private:
+      __format::__formatter_fp<_CharT> _M_f;
+    };
+#endif
+#endif // __cpp_lib_to_chars
+
   /** Format a pointer.
    * @{
    */
@@ -2702,7 +2837,6 @@ namespace __format
 	__int128 _M_i128;
 	unsigned __int128 _M_u128;
 #endif
-	// TODO _Float16 etc.
 #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
 	__ieee128 _M_f128;
 	__ibm128  _M_ibm128;
@@ -2931,7 +3065,15 @@ namespace __format
 	    return type_identity<__ieee128>();
 #endif
 
-	  // TODO bfloat16 and float16
+#if defined(__FLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+	  else if constexpr (is_same_v<_Td, _Float16>)
+	    return type_identity<float>();
+#endif
+
+#if defined(__BFLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+	  else if constexpr (is_same_v<_Td, decltype(0.0bf16)>)
+	    return type_identity<float>();
+#endif
 
 #ifdef __FLT32_DIG__
 	  else if constexpr (is_same_v<_Td, _Float32>)
diff --git a/libstdc++-v3/testsuite/std/format/formatter/ext_float.cc b/libstdc++-v3/testsuite/std/format/formatter/ext_float.cc
new file mode 100644
index 00000000000..89810295b64
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/format/formatter/ext_float.cc
@@ -0,0 +1,92 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+#include <format>
+#include <testsuite_hooks.h>
+
+template<typename T>
+bool format_float()
+{
+    auto s = std::format("{:#} != {:<+7.3f}", (T)-0.0, (T)0.5);
+    return s == "-0. != +0.500 ";
+}
+
+#if __cplusplus > 202002L
+template<typename T>
+concept formattable = std::formattable<T, char>;
+#else
+template<typename T>
+concept formattable = std::semiregular<std::formatter<T, char>>;
+#endif
+
+void
+test_float16()
+{
+#if __FLT16_DIG__
+  if constexpr (formattable<_Float16>)
+    VERIFY( format_float<_Float16>() );
+  else
+    std::puts("Cannot format _Float16 on this target");
+#endif
+}
+
+void
+test_float32()
+{
+#if __FLT32_DIG__
+  if constexpr (formattable<_Float32>)
+    VERIFY( format_float<_Float32>() );
+  else
+    std::puts("Cannot format _Float32 on this target");
+#endif
+}
+
+void
+test_float64()
+{
+#if __FLT64_DIG__
+  if constexpr (formattable<_Float64>)
+    VERIFY( format_float<_Float64>() );
+  else
+    std::puts("Cannot format _Float64 on this target");
+#endif
+}
+
+void
+test_float128()
+{
+#ifdef __SIZEOF_FLOAT128__
+  if constexpr (formattable<__float128>)
+    VERIFY( format_float<__float128>() );
+  else
+    std::puts("Cannot format __float128 on this target");
+#endif
+#if __FLT128_DIG__
+  if constexpr (formattable<_Float128>)
+    VERIFY( format_float<_Float128>() );
+  else
+    std::puts("Cannot format _Float128 on this target");
+#endif
+}
+
+void
+test_bfloat16()
+{
+#if __BFLT16_DIG__
+  using bfloat16_t = decltype(0.0bf16);
+
+  if constexpr (formattable<bfloat16_t>)
+    VERIFY( format_float<bfloat16_t>() );
+  else
+    std::puts("Cannot format bfloat16_t on this target");
+#endif
+}
+
+int main()
+{
+  test_float16();
+  test_float32();
+  test_float64();
+  test_float128();
+  test_bfloat16();
+}
diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc
index 59d327fccee..5141cbd11bf 100644
--- a/libstdc++-v3/testsuite/std/format/functions/format.cc
+++ b/libstdc++-v3/testsuite/std/format/functions/format.cc
@@ -347,38 +347,6 @@ test_p1652r1() // printf corner cases in std::format
   VERIFY( s == "3.31" );
 }
 
-template<typename T>
-bool format_float()
-{
-    auto s = std::format("{:#} != {:<+7.3f}", (T)-0.0, (T)0.5);
-    return s == "-0. != +0.500 ";
-}
-
-#if __cplusplus > 202002L
-template<typename T>
-concept formattable = std::formattable<T, char>;
-#else
-template<typename T>
-concept formattable = requires (T t, char* p) { std::to_chars(p, p, t); };
-#endif
-
-void
-test_float128()
-{
-#ifdef __SIZEOF_FLOAT128__
-  if constexpr (formattable<__float128>)
-    VERIFY( format_float<__float128>() );
-  else
-    std::puts("Cannot format __float128 on this target");
-#endif
-#if __FLT128_DIG__
-  if constexpr (formattable<_Float128>)
-    VERIFY( format_float<_Float128>() );
-  else
-    std::puts("Cannot format _Float128 on this target");
-#endif
-}
-
 void
 test_pointer()
 {
@@ -429,6 +397,5 @@ int main()
   test_wchar();
   test_minmax();
   test_p1652r1();
-  test_float128();
   test_pointer();
 }
-- 
2.41.0


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-08-17 20:32 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-17 20:32 [committed] libstdc++: Add std::formatter specializations for extended float types Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).