[libcxx-commits] [libcxx] [libc++] Refactor the <charconv> tests (PR #227611)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Wed Sep 30 01:59:51 PDT 2026


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/227611

None

>From f1139eca1f84570d5a6683b505c78c9e6ac5c737 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 30 Sep 2026 10:59:21 +0200
Subject: [PATCH] [libc++] Refactor the <charconv> tests

---
 .../floating_point.pass.cpp                   | 1862 ++++++++---------
 .../integral.bool.verify.cpp                  |   32 -
 .../charconv.from.chars/integral.pass.cpp     |   28 +-
 .../integral.roundtrip.pass.cpp               |  142 +-
 .../integral.bool.verify.cpp                  |   32 -
 .../charconv.to.chars/integral.pass.cpp       |  599 ++++--
 libcxx/test/support/charconv_test_helpers.h   |  271 +--
 7 files changed, 1409 insertions(+), 1557 deletions(-)
 delete mode 100644 libcxx/test/std/utilities/charconv/charconv.from.chars/integral.bool.verify.cpp
 delete mode 100644 libcxx/test/std/utilities/charconv/charconv.to.chars/integral.bool.verify.cpp

diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp
index a02c7ac466f04..fa223785d758d 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp
@@ -17,6 +17,7 @@
 //                              double& value, chars_format fmt = chars_format::general)
 
 #include <array>
+#include <cassert>
 #include <charconv>
 #include <cmath>
 #include <cstring>
@@ -24,8 +25,8 @@
 #include <stdexcept>
 #include <system_error>
 
-#include "charconv_test_helpers.h"
 #include "test_macros.h"
+#include "type_algorithms.h"
 
 template <class F>
 void test_infinity(std::chars_format fmt) {
@@ -326,1193 +327,1183 @@ void test_fmt_independent(std::chars_format fmt) {
 }
 
 template <class F>
-struct test_basics {
-  void operator()() {
-    for (auto fmt : {std::chars_format::scientific,
-                     std::chars_format::fixed,
-                     /*std::chars_format::hex,*/ std::chars_format::general})
-      test_fmt_independent<F>(fmt);
-  }
-};
+void test_basics() {
+  for (auto fmt : {std::chars_format::scientific,
+                   std::chars_format::fixed,
+                   /*std::chars_format::hex,*/ std::chars_format::general})
+    test_fmt_independent<F>(fmt);
+}
 
 template <class F>
-struct test_fixed {
-  void operator()() {
-    std::from_chars_result r;
-    F x = 0.25;
+void test_fixed() {
+  std::from_chars_result r;
+  F x = 0.25;
 
-    // *** Failures
+  // *** Failures
 
-    { // Starts with invalid character
-      std::array s = {' ', '1'};
-      for (auto c : "abcdefghijklmnopqrstuvwxyz"
-                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                    "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") {
-        s[0] = c;
-        r    = std::from_chars(s.data(), s.data() + s.size(), x, std::chars_format::fixed);
+  { // Starts with invalid character
+    std::array s = {' ', '1'};
+    for (auto c : "abcdefghijklmnopqrstuvwxyz"
+                  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                  "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") {
+      s[0] = c;
+      r    = std::from_chars(s.data(), s.data() + s.size(), x, std::chars_format::fixed);
 
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s.data());
-        assert(x == F(0.25));
-      }
+      assert(r.ec == std::errc::invalid_argument);
+      assert(r.ptr == s.data());
+      assert(x == F(0.25));
     }
+  }
 
-    // *** Success
+  // *** Success
 
-    { // number followed by non-numeric values
-      const char* s = "001x";
+  { // number followed by non-numeric values
+    const char* s = "001x";
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 3);
-      assert(x == F(1.0));
-    }
-    { // no leading digit
-      const char* s = ".5";
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(1.0));
+  }
+  { // no leading digit
+    const char* s = ".5";
+
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 2);
+    assert(x == F(0.5));
+  }
+  { // negative sign and no leading digit
+    const char* s = "-.5";
+
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(-0.5));
+  }
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 2);
-      assert(x == F(0.5));
-    }
-    { // negative sign and no leading digit
-      const char* s = "-.5";
+  { // double decimal point
+    const char* s = "1.25.78";
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 3);
-      assert(x == F(-0.5));
-    }
+    // This number is halfway between two float values.
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 4);
+    assert(x == F(1.25));
+  }
+  { // exponent no sign
+    const char* s = "1.5e10";
+    r             = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(1.5));
+  }
+  { // exponent capitalized no sign
+    const char* s = "1.5E10";
+    r             = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(1.5));
+  }
+  { // exponent + sign
+    const char* s = "1.5e+10";
+    r             = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(1.5));
+  }
+  { // exponent - sign
+    const char* s = "1.5e-10";
+    r             = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(1.5));
+  }
+  { // Exponent no number
+    const char* s = "1.5e";
 
-    { // double decimal point
-      const char* s = "1.25.78";
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(1.5));
+  }
+  { // Exponent sign no number
+    {
+      const char* s = "1.5e+";
 
-      // This number is halfway between two float values.
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 4);
-      assert(x == F(1.25));
+      assert(r.ptr == s + 3);
+      assert(x == F(1.5));
     }
-    { // exponent no sign
-      const char* s = "1.5e10";
-      r             = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+    {
+      const char* s = "1.5e-";
 
+      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + 3);
       assert(x == F(1.5));
     }
-    { // exponent capitalized no sign
-      const char* s = "1.5E10";
-      r             = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+  }
+  { // Exponent with whitespace
+    {
+      const char* s = "1.5e +1";
 
+      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + 3);
       assert(x == F(1.5));
     }
-    { // exponent + sign
-      const char* s = "1.5e+10";
-      r             = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+    {
+      const char* s = "1.5e+ 1";
 
+      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + 3);
       assert(x == F(1.5));
     }
-    { // exponent - sign
-      const char* s = "1.5e-10";
-      r             = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+    {
+      const char* s = "1.5e -1";
 
+      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + 3);
       assert(x == F(1.5));
     }
-    { // Exponent no number
-      const char* s = "1.5e";
+    {
+      const char* s = "1.5e- 1";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + 3);
       assert(x == F(1.5));
     }
-    { // Exponent sign no number
-      {
-        const char* s = "1.5e+";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(1.5));
-      }
-      {
-        const char* s = "1.5e-";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(1.5));
-      }
-    }
-    { // Exponent with whitespace
-      {
-        const char* s = "1.5e +1";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(1.5));
-      }
-      {
-        const char* s = "1.5e+ 1";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(1.5));
-      }
-      {
-        const char* s = "1.5e -1";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(1.5));
-      }
-      {
-        const char* s = "1.5e- 1";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(1.5));
-      }
-    }
-    { // double exponent
-      const char* s = "1.25e0e12";
-      r             = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+  }
+  { // double exponent
+    const char* s = "1.25e0e12";
+    r             = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
 
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 4);
+    assert(x == F(1.25));
+  }
+  { // Exponent double sign
+    {
+      const char* s = "1.25e++12";
+
+      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + 4);
       assert(x == F(1.25));
     }
-    { // Exponent double sign
-      {
-        const char* s = "1.25e++12";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 4);
-        assert(x == F(1.25));
-      }
-      {
-        const char* s = "1.25e+-12";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 4);
-        assert(x == F(1.25));
-      }
-      {
-        const char* s = "1.25e-+12";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 4);
-        assert(x == F(1.25));
-      }
-      {
-        const char* s = "1.25e--12";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 4);
-        assert(x == F(1.25));
-      }
-    }
-    { // exponent hex prefix
-      const char* s = "1.25e0x12";
+    {
+      const char* s = "1.25e+-12";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + 4);
       assert(x == F(1.25));
     }
-    { // This number is halfway between two float values.
-      const char* s = "20040229";
+    {
+      const char* s = "1.25e-+12";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 8);
-      assert(x == F(20040229));
+      assert(r.ptr == s + 4);
+      assert(x == F(1.25));
     }
-    { // Shifting mantissa exponent and no exponent
-      const char* s = "123.456";
+    {
+      const char* s = "1.25e--12";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 7);
-      assert(x == F(1.23456e2));
+      assert(r.ptr == s + 4);
+      assert(x == F(1.25));
     }
-    { // Shifting mantissa exponent and an exponent
-      const char* s = "123.456e3";
-      r             = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+  }
+  { // exponent hex prefix
+    const char* s = "1.25e0x12";
 
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 4);
+    assert(x == F(1.25));
+  }
+  { // This number is halfway between two float values.
+    const char* s = "20040229";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 8);
+    assert(x == F(20040229));
+  }
+  { // Shifting mantissa exponent and no exponent
+    const char* s = "123.456";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 7);
+    assert(x == F(1.23456e2));
+  }
+  { // Shifting mantissa exponent and an exponent
+    const char* s = "123.456e3";
+    r             = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 7);
+    assert(x == F(123.456));
+  }
+  { // Mantissa overflow
+    {
+      const char* s = "0.111111111111111111111111111111111111111111";
+
+      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 7);
-      assert(x == F(123.456));
-    }
-    { // Mantissa overflow
-      {
-        const char* s = "0.111111111111111111111111111111111111111111";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + std::strlen(s));
-        assert(x == F(0.111111111111111111111111111111111111111111));
-      }
-      {
-        const char* s = "111111111111.111111111111111111111111111111111111111111";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + std::strlen(s));
-        assert(x == F(111111111111.111111111111111111111111111111111111111111));
-      }
-    }
-    { // Negative value
-      const char* s = "-0.25";
+      assert(r.ptr == s + std::strlen(s));
+      assert(x == F(0.111111111111111111111111111111111111111111));
+    }
+    {
+      const char* s = "111111111111.111111111111111111111111111111111111111111";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + std::strlen(s));
-      assert(x == F(-0.25));
+      assert(x == F(111111111111.111111111111111111111111111111111111111111));
     }
   }
-};
+  { // Negative value
+    const char* s = "-0.25";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + std::strlen(s));
+    assert(x == F(-0.25));
+  }
+}
 
 template <class F>
-struct test_scientific {
-  void operator()() {
-    std::from_chars_result r;
-    F x = 0.25;
+void test_scientific() {
+  std::from_chars_result r;
+  F x = 0.25;
 
-    // *** Failures
+  // *** Failures
 
-    { // Starts with invalid character
-      std::array s = {' ', '1', 'e', '0'};
-      for (auto c : "abcdefghijklmnopqrstuvwxyz"
-                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                    "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") {
-        s[0] = c;
-        r    = std::from_chars(s.data(), s.data() + s.size(), x, std::chars_format::scientific);
+  { // Starts with invalid character
+    std::array s = {' ', '1', 'e', '0'};
+    for (auto c : "abcdefghijklmnopqrstuvwxyz"
+                  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                  "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") {
+      s[0] = c;
+      r    = std::from_chars(s.data(), s.data() + s.size(), x, std::chars_format::scientific);
 
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s.data());
-        assert(x == F(0.25));
-      }
+      assert(r.ec == std::errc::invalid_argument);
+      assert(r.ptr == s.data());
+      assert(x == F(0.25));
     }
-    { // No exponent
-      const char* s = "1.23";
-      r             = std::from_chars(s, s + strlen(s), x, std::chars_format::scientific);
+  }
+  { // No exponent
+    const char* s = "1.23";
+    r             = std::from_chars(s, s + strlen(s), x, std::chars_format::scientific);
 
+    assert(r.ec == std::errc::invalid_argument);
+    assert(r.ptr == s);
+    assert(x == F(0.25));
+  }
+  { // Exponent no number
+    const char* s = "1.23e";
+    r             = std::from_chars(s, s + strlen(s), x, std::chars_format::scientific);
+
+    assert(r.ec == std::errc::invalid_argument);
+    assert(r.ptr == s);
+    assert(x == F(0.25));
+  }
+  { // Exponent sign no number
+    {
+      const char* s = "1.5e+";
+
+      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
       assert(r.ec == std::errc::invalid_argument);
       assert(r.ptr == s);
       assert(x == F(0.25));
     }
-    { // Exponent no number
-      const char* s = "1.23e";
-      r             = std::from_chars(s, s + strlen(s), x, std::chars_format::scientific);
+    {
+      const char* s = "1.5e-";
 
+      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
       assert(r.ec == std::errc::invalid_argument);
       assert(r.ptr == s);
       assert(x == F(0.25));
     }
-    { // Exponent sign no number
-      {
-        const char* s = "1.5e+";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s);
-        assert(x == F(0.25));
-      }
-      {
-        const char* s = "1.5e-";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s);
-        assert(x == F(0.25));
-      }
-    }
-    { // Exponent with whitespace
-      {
-        const char* s = "1.5e +1";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s);
-        assert(x == F(0.25));
-      }
-      {
-        const char* s = "1.5e+ 1";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s);
-        assert(x == F(0.25));
-      }
-      {
-        const char* s = "1.5e -1";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s);
-        assert(x == F(0.25));
-      }
-      {
-        const char* s = "1.5e- 1";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s);
-        assert(x == F(0.25));
-      }
-    }
-    { // exponent double sign
-      {
-        const char* s = "1.25e++12";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s);
-        assert(x == F(0.25));
-      }
-      {
-        const char* s = "1.25e+-12";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s);
-        assert(x == F(0.25));
-      }
-      {
-        const char* s = "1.25e-+12";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s);
-        assert(x == F(0.25));
-      }
-      {
-        const char* s = "1.25e--12";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s);
-        assert(x == F(0.25));
-      }
-    }
-
-    // *** Success
-
-    { // number followed by non-numeric values
-      const char* s = "001e0x";
-
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 5);
-      assert(x == F(1.0));
-    }
-
-    { // double decimal point
-      const char* s = "1.25e0.78";
+  }
+  { // Exponent with whitespace
+    {
+      const char* s = "1.5e +1";
 
-      // This number is halfway between two float values.
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
-      assert(x == F(1.25));
+      assert(r.ec == std::errc::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
     }
-
-    { // exponent no sign
-      const char* s = "1.5e10";
+    {
+      const char* s = "1.5e+ 1";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
-      assert(x == F(1.5e10));
+      assert(r.ec == std::errc::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
     }
-    { // exponent capitalized no sign
-      const char* s = "1.5E10";
+    {
+      const char* s = "1.5e -1";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
-      assert(x == F(1.5e10));
+      assert(r.ec == std::errc::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
     }
-    { // exponent + sign
-      const char* s = "1.5e+10";
+    {
+      const char* s = "1.5e- 1";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 7);
-      assert(x == F(1.5e10));
+      assert(r.ec == std::errc::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
     }
-    { // exponent - sign
-      const char* s = "1.5e-10";
+  }
+  { // exponent double sign
+    {
+      const char* s = "1.25e++12";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 7);
-      assert(x == F(1.5e-10));
+      assert(r.ec == std::errc::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
     }
-    { // exponent hex prefix -> e0
-      const char* s = "1.25e0x12";
+    {
+      const char* s = "1.25e+-12";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
-      assert(x == F(1.25));
+      assert(r.ec == std::errc::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
     }
-    { // double exponent
-      const char* s = "1.25e0e12";
+    {
+      const char* s = "1.25e-+12";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
-      assert(x == F(1.25));
+      assert(r.ec == std::errc::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
     }
-    { // This number is halfway between two float values.
-      const char* s = "20040229e0";
+    {
+      const char* s = "1.25e--12";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 10);
-      assert(x == F(20040229));
+      assert(r.ec == std::errc::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
     }
-    { // Shifting mantissa exponent and an exponent
-      const char* s = "123.456e3";
+  }
 
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 9);
-      assert(x == F(1.23456e5));
-    }
-    { // Mantissa overflow
-      {
-        const char* s = "0.111111111111111111111111111111111111111111e0";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + std::strlen(s));
-        assert(x == F(0.111111111111111111111111111111111111111111));
-      }
-      {
-        const char* s = "111111111111.111111111111111111111111111111111111111111e0";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + std::strlen(s));
-        assert(x == F(111111111111.111111111111111111111111111111111111111111));
-      }
-    }
-    { // Negative value
-      const char* s = "-0.25e0";
+  // *** Success
+
+  { // number followed by non-numeric values
+    const char* s = "001e0x";
+
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 5);
+    assert(x == F(1.0));
+  }
+
+  { // double decimal point
+    const char* s = "1.25e0.78";
+
+    // This number is halfway between two float values.
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(1.25));
+  }
+
+  { // exponent no sign
+    const char* s = "1.5e10";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(1.5e10));
+  }
+  { // exponent capitalized no sign
+    const char* s = "1.5E10";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(1.5e10));
+  }
+  { // exponent + sign
+    const char* s = "1.5e+10";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 7);
+    assert(x == F(1.5e10));
+  }
+  { // exponent - sign
+    const char* s = "1.5e-10";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 7);
+    assert(x == F(1.5e-10));
+  }
+  { // exponent hex prefix -> e0
+    const char* s = "1.25e0x12";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(1.25));
+  }
+  { // double exponent
+    const char* s = "1.25e0e12";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(1.25));
+  }
+  { // This number is halfway between two float values.
+    const char* s = "20040229e0";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 10);
+    assert(x == F(20040229));
+  }
+  { // Shifting mantissa exponent and an exponent
+    const char* s = "123.456e3";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 9);
+    assert(x == F(1.23456e5));
+  }
+  { // Mantissa overflow
+    {
+      const char* s = "0.111111111111111111111111111111111111111111e0";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + std::strlen(s));
-      assert(x == F(-0.25));
+      assert(x == F(0.111111111111111111111111111111111111111111));
     }
-    { // value is too big -> +inf
-      const char* s = "1e9999999999999999999999999999999999999999";
+    {
+      const char* s = "111111111111.111111111111111111111111111111111111111111e0";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc::result_out_of_range);
-      assert(r.ptr == s + strlen(s));
-      assert(x == std::numeric_limits<F>::infinity());
+      assert(r.ec == std::errc{});
+      assert(r.ptr == s + std::strlen(s));
+      assert(x == F(111111111111.111111111111111111111111111111111111111111));
     }
-    { // negative value is too big -> -inf
-      const char* s = "-1e9999999999999999999999999999999999999999";
+  }
+  { // Negative value
+    const char* s = "-0.25e0";
 
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc::result_out_of_range);
-      assert(r.ptr == s + strlen(s));
-      assert(x == -std::numeric_limits<F>::infinity());
-    }
-    { // value is too small -> 0
-      const char* s = "1e-9999999999999999999999999999999999999999";
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + std::strlen(s));
+    assert(x == F(-0.25));
+  }
+  { // value is too big -> +inf
+    const char* s = "1e9999999999999999999999999999999999999999";
 
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc::result_out_of_range);
-      assert(r.ptr == s + strlen(s));
-      assert(x == F(0.0));
-    }
-    { // negative value is too small -> -0
-      const char* s = "-1e-9999999999999999999999999999999999999999";
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc::result_out_of_range);
+    assert(r.ptr == s + strlen(s));
+    assert(x == std::numeric_limits<F>::infinity());
+  }
+  { // negative value is too big -> -inf
+    const char* s = "-1e9999999999999999999999999999999999999999";
 
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
-      assert(r.ec == std::errc::result_out_of_range);
-      assert(r.ptr == s + strlen(s));
-      assert(x == F(-0.0));
-    }
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc::result_out_of_range);
+    assert(r.ptr == s + strlen(s));
+    assert(x == -std::numeric_limits<F>::infinity());
   }
-};
+  { // value is too small -> 0
+    const char* s = "1e-9999999999999999999999999999999999999999";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc::result_out_of_range);
+    assert(r.ptr == s + strlen(s));
+    assert(x == F(0.0));
+  }
+  { // negative value is too small -> -0
+    const char* s = "-1e-9999999999999999999999999999999999999999";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+    assert(r.ec == std::errc::result_out_of_range);
+    assert(r.ptr == s + strlen(s));
+    assert(x == F(-0.0));
+  }
+}
 
 template <class F>
-struct test_general {
-  void operator()() {
-    std::from_chars_result r;
-    F x = 0.25;
+void test_general() {
+  std::from_chars_result r;
+  F x = 0.25;
 
-    // *** Failures
+  // *** Failures
 
-    { // Starts with invalid character
-      std::array s = {' ', '1'};
-      for (auto c : "abcdefghijklmnopqrstuvwxyz"
-                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                    "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") {
-        s[0] = c;
-        r    = std::from_chars(s.data(), s.data() + s.size(), x);
+  { // Starts with invalid character
+    std::array s = {' ', '1'};
+    for (auto c : "abcdefghijklmnopqrstuvwxyz"
+                  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                  "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") {
+      s[0] = c;
+      r    = std::from_chars(s.data(), s.data() + s.size(), x);
 
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s.data());
-        assert(x == F(0.25));
-      }
+      assert(r.ec == std::errc::invalid_argument);
+      assert(r.ptr == s.data());
+      assert(x == F(0.25));
     }
+  }
 
-    // *** Success
+  // *** Success
 
-    { // number followed by non-numeric values
-      const char* s = "001x";
+  { // number followed by non-numeric values
+    const char* s = "001x";
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 3);
-      assert(x == F(1.0));
-    }
-    { // no leading digit
-      const char* s = ".5e0";
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(1.0));
+  }
+  { // no leading digit
+    const char* s = ".5e0";
+
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 4);
+    assert(x == F(0.5));
+  }
+  { // negative sign and no leading digit
+    const char* s = "-.5e0";
+
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 5);
+    assert(x == F(-0.5));
+  }
+  { // no leading digit
+    const char* s = ".5";
+
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 2);
+    assert(x == F(0.5));
+  }
+  { // negative sign and no leading digit
+    const char* s = "-.5";
+
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(-0.5));
+  }
+  { // double decimal point
+    const char* s = "1.25.78";
+
+    // This number is halfway between two float values.
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 4);
+    assert(x == F(1.25));
+  }
+  { // exponent no sign
+    const char* s = "1.5e10";
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 4);
-      assert(x == F(0.5));
-    }
-    { // negative sign and no leading digit
-      const char* s = "-.5e0";
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(1.5e10));
+  }
+  { // exponent capitalized no sign
+    const char* s = "1.5E10";
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 5);
-      assert(x == F(-0.5));
-    }
-    { // no leading digit
-      const char* s = ".5";
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(1.5e10));
+  }
+  { // exponent + sign
+    const char* s = "1.5e+10";
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 2);
-      assert(x == F(0.5));
-    }
-    { // negative sign and no leading digit
-      const char* s = "-.5";
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 7);
+    assert(x == F(1.5e10));
+  }
+  { // exponent - sign
+    const char* s = "1.5e-10";
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 3);
-      assert(x == F(-0.5));
-    }
-    { // double decimal point
-      const char* s = "1.25.78";
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 7);
+    assert(x == F(1.5e-10));
+  }
+  { // Exponent no number
+    const char* s = "1.5e";
+
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(1.5));
+  }
+  { // Exponent sign no number
+    {
+      const char* s = "1.5e+";
 
-      // This number is halfway between two float values.
       r = std::from_chars(s, s + std::strlen(s), x);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 4);
-      assert(x == F(1.25));
+      assert(r.ptr == s + 3);
+      assert(x == F(1.5));
     }
-    { // exponent no sign
-      const char* s = "1.5e10";
+    {
+      const char* s = "1.5e-";
 
       r = std::from_chars(s, s + std::strlen(s), x);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
-      assert(x == F(1.5e10));
+      assert(r.ptr == s + 3);
+      assert(x == F(1.5));
     }
-    { // exponent capitalized no sign
-      const char* s = "1.5E10";
+  }
+  { // Exponent with whitespace
+    {
+      const char* s = "1.5e +1";
 
       r = std::from_chars(s, s + std::strlen(s), x);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
-      assert(x == F(1.5e10));
+      assert(r.ptr == s + 3);
+      assert(x == F(1.5));
     }
-    { // exponent + sign
-      const char* s = "1.5e+10";
+    {
+      const char* s = "1.5e+ 1";
 
       r = std::from_chars(s, s + std::strlen(s), x);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 7);
-      assert(x == F(1.5e10));
+      assert(r.ptr == s + 3);
+      assert(x == F(1.5));
     }
-    { // exponent - sign
-      const char* s = "1.5e-10";
+    {
+      const char* s = "1.5e -1";
 
       r = std::from_chars(s, s + std::strlen(s), x);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 7);
-      assert(x == F(1.5e-10));
+      assert(r.ptr == s + 3);
+      assert(x == F(1.5));
     }
-    { // Exponent no number
-      const char* s = "1.5e";
+    {
+      const char* s = "1.5e- 1";
 
       r = std::from_chars(s, s + std::strlen(s), x);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + 3);
       assert(x == F(1.5));
     }
-    { // Exponent sign no number
-      {
-        const char* s = "1.5e+";
-
-        r = std::from_chars(s, s + std::strlen(s), x);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(1.5));
-      }
-      {
-        const char* s = "1.5e-";
-
-        r = std::from_chars(s, s + std::strlen(s), x);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(1.5));
-      }
-    }
-    { // Exponent with whitespace
-      {
-        const char* s = "1.5e +1";
-
-        r = std::from_chars(s, s + std::strlen(s), x);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(1.5));
-      }
-      {
-        const char* s = "1.5e+ 1";
-
-        r = std::from_chars(s, s + std::strlen(s), x);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(1.5));
-      }
-      {
-        const char* s = "1.5e -1";
-
-        r = std::from_chars(s, s + std::strlen(s), x);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(1.5));
-      }
-      {
-        const char* s = "1.5e- 1";
-
-        r = std::from_chars(s, s + std::strlen(s), x);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(1.5));
-      }
-    }
-    { // exponent double sign
-      {
-        const char* s = "1.25e++12";
-
-        r = std::from_chars(s, s + std::strlen(s), x);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 4);
-        assert(x == F(1.25));
-      }
-      {
-        const char* s = "1.25e+-12";
-
-        r = std::from_chars(s, s + std::strlen(s), x);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 4);
-        assert(x == F(1.25));
-      }
-      {
-        const char* s = "1.25e-+12";
-
-        r = std::from_chars(s, s + std::strlen(s), x);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 4);
-        assert(x == F(1.25));
-      }
-      {
-        const char* s = "1.25e--12";
-
-        r = std::from_chars(s, s + std::strlen(s), x);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 4);
-        assert(x == F(1.25));
-      }
-    }
-    { // exponent hex prefix -> e0
-      const char* s = "1.25e0x12";
+  }
+  { // exponent double sign
+    {
+      const char* s = "1.25e++12";
 
       r = std::from_chars(s, s + std::strlen(s), x);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
+      assert(r.ptr == s + 4);
       assert(x == F(1.25));
     }
-    { // double exponent
-      const char* s = "1.25e0e12";
+    {
+      const char* s = "1.25e+-12";
 
       r = std::from_chars(s, s + std::strlen(s), x);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
+      assert(r.ptr == s + 4);
       assert(x == F(1.25));
     }
-    { // This number is halfway between two float values.
-      const char* s = "20040229";
+    {
+      const char* s = "1.25e-+12";
 
       r = std::from_chars(s, s + std::strlen(s), x);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 8);
-      assert(x == F(20040229));
+      assert(r.ptr == s + 4);
+      assert(x == F(1.25));
     }
-    { // Shifting mantissa exponent and no exponent
-      const char* s = "123.456";
+    {
+      const char* s = "1.25e--12";
 
       r = std::from_chars(s, s + std::strlen(s), x);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 7);
-      assert(x == F(1.23456e2));
+      assert(r.ptr == s + 4);
+      assert(x == F(1.25));
     }
-    { // Shifting mantissa exponent and an exponent
-      const char* s = "123.456e3";
+  }
+  { // exponent hex prefix -> e0
+    const char* s = "1.25e0x12";
 
-      r = std::from_chars(s, s + std::strlen(s), x);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 9);
-      assert(x == F(1.23456e5));
-    }
-    { // Mantissa overflow
-      {
-        const char* s = "0.111111111111111111111111111111111111111111";
-
-        r = std::from_chars(s, s + std::strlen(s), x);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + std::strlen(s));
-        assert(x == F(0.111111111111111111111111111111111111111111));
-      }
-      {
-        const char* s = "111111111111.111111111111111111111111111111111111111111";
-
-        r = std::from_chars(s, s + std::strlen(s), x);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + std::strlen(s));
-        assert(x == F(111111111111.111111111111111111111111111111111111111111));
-      }
-    }
-    { // Negative value
-      const char* s = "-0.25";
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(1.25));
+  }
+  { // double exponent
+    const char* s = "1.25e0e12";
+
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(1.25));
+  }
+  { // This number is halfway between two float values.
+    const char* s = "20040229";
+
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 8);
+    assert(x == F(20040229));
+  }
+  { // Shifting mantissa exponent and no exponent
+    const char* s = "123.456";
+
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 7);
+    assert(x == F(1.23456e2));
+  }
+  { // Shifting mantissa exponent and an exponent
+    const char* s = "123.456e3";
+
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 9);
+    assert(x == F(1.23456e5));
+  }
+  { // Mantissa overflow
+    {
+      const char* s = "0.111111111111111111111111111111111111111111";
 
       r = std::from_chars(s, s + std::strlen(s), x);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + std::strlen(s));
-      assert(x == F(-0.25));
+      assert(x == F(0.111111111111111111111111111111111111111111));
     }
-    { // value is too big -> +inf
-      const char* s = "1e9999999999999999999999999999999999999999";
+    {
+      const char* s = "111111111111.111111111111111111111111111111111111111111";
 
       r = std::from_chars(s, s + std::strlen(s), x);
-      assert(r.ec == std::errc::result_out_of_range);
-      assert(r.ptr == s + strlen(s));
-      assert(x == std::numeric_limits<F>::infinity());
+      assert(r.ec == std::errc{});
+      assert(r.ptr == s + std::strlen(s));
+      assert(x == F(111111111111.111111111111111111111111111111111111111111));
     }
-    { // negative value is too big -> -inf
-      const char* s = "-1e9999999999999999999999999999999999999999";
+  }
+  { // Negative value
+    const char* s = "-0.25";
 
-      r = std::from_chars(s, s + std::strlen(s), x);
-      assert(r.ec == std::errc::result_out_of_range);
-      assert(r.ptr == s + strlen(s));
-      assert(x == -std::numeric_limits<F>::infinity());
-    }
-    { // value is too small -> 0
-      const char* s = "1e-9999999999999999999999999999999999999999";
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + std::strlen(s));
+    assert(x == F(-0.25));
+  }
+  { // value is too big -> +inf
+    const char* s = "1e9999999999999999999999999999999999999999";
 
-      r = std::from_chars(s, s + std::strlen(s), x);
-      assert(r.ec == std::errc::result_out_of_range);
-      assert(r.ptr == s + strlen(s));
-      assert(x == F(0.0));
-    }
-    { // negative value is too small -> -0
-      const char* s = "-1e-9999999999999999999999999999999999999999";
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc::result_out_of_range);
+    assert(r.ptr == s + strlen(s));
+    assert(x == std::numeric_limits<F>::infinity());
+  }
+  { // negative value is too big -> -inf
+    const char* s = "-1e9999999999999999999999999999999999999999";
 
-      r = std::from_chars(s, s + std::strlen(s), x);
-      assert(r.ec == std::errc::result_out_of_range);
-      assert(r.ptr == s + strlen(s));
-      assert(x == F(-0.0));
-    }
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc::result_out_of_range);
+    assert(r.ptr == s + strlen(s));
+    assert(x == -std::numeric_limits<F>::infinity());
   }
-};
+  { // value is too small -> 0
+    const char* s = "1e-9999999999999999999999999999999999999999";
+
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc::result_out_of_range);
+    assert(r.ptr == s + strlen(s));
+    assert(x == F(0.0));
+  }
+  { // negative value is too small -> -0
+    const char* s = "-1e-9999999999999999999999999999999999999999";
+
+    r = std::from_chars(s, s + std::strlen(s), x);
+    assert(r.ec == std::errc::result_out_of_range);
+    assert(r.ptr == s + strlen(s));
+    assert(x == F(-0.0));
+  }
+}
 
 template <class F>
-struct test_hex {
-  void operator()() {
-    std::from_chars_result r;
-    F x = 0.25;
+void test_hex() {
+  std::from_chars_result r;
+  F x = 0.25;
 
-    // *** Failures
+  // *** Failures
 
-    { // Starts with invalid character
-      std::array s = {' ', '1', 'e', '0'};
-      for (auto c : "ghijklmnopqrstuvwxyz"
-                    "GHIJKLMNOPQRSTUVWXYZ"
-                    "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") {
-        s[0] = c;
-        r    = std::from_chars(s.data(), s.data() + s.size(), x, std::chars_format::hex);
+  { // Starts with invalid character
+    std::array s = {' ', '1', 'e', '0'};
+    for (auto c : "ghijklmnopqrstuvwxyz"
+                  "GHIJKLMNOPQRSTUVWXYZ"
+                  "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") {
+      s[0] = c;
+      r    = std::from_chars(s.data(), s.data() + s.size(), x, std::chars_format::hex);
 
-        assert(r.ec == std::errc::invalid_argument);
-        assert(r.ptr == s.data());
-        assert(x == F(0.25));
-      }
+      assert(r.ec == std::errc::invalid_argument);
+      assert(r.ptr == s.data());
+      assert(x == F(0.25));
     }
+  }
 
-    // *** Success
+  // *** Success
 
-    { // number followed by non-numeric values
-      const char* s = "001x";
+  { // number followed by non-numeric values
+    const char* s = "001x";
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 3);
-      assert(x == F(1.0));
-    }
-    { // no leading digit
-      const char* s = ".5p0";
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(1.0));
+  }
+  { // no leading digit
+    const char* s = ".5p0";
+
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 4);
+    assert(x == F(0x0.5p0));
+  }
+  { // negative sign and no leading digit
+    const char* s = "-.5p0";
+
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 5);
+    assert(x == F(-0x0.5p0));
+  }
+  { // no leading digit
+    const char* s = ".5";
+
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 2);
+    assert(x == F(0x0.5p0));
+  }
+  { // negative sign and no leading digit
+    const char* s = "-.5";
+
+    // the expected form of the subject sequence is a nonempty sequence of
+    // decimal digits optionally containing a decimal-point character, then
+    // an optional exponent part as defined in 6.4.4.3, excluding any digit
+    // separators (6.4.4.2); (C23 7.24.1.5)
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(-0x0.5p0));
+  }
+  { // double decimal point
+    const char* s = "1.25.78";
+
+    // This number is halfway between two float values.
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 4);
+    assert(x == F(0x1.25p0));
+  }
+  { // exponent no sign
+    const char* s = "1.5p10";
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 4);
-      assert(x == F(0x0.5p0));
-    }
-    { // negative sign and no leading digit
-      const char* s = "-.5p0";
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(0x1.5p10));
+  }
+  { // exponent capitalized no sign
+    const char* s = "1.5P10";
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 5);
-      assert(x == F(-0x0.5p0));
-    }
-    { // no leading digit
-      const char* s = ".5";
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(0x1.5p10));
+  }
+  { // exponent + sign
+    const char* s = "1.5p+10";
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 2);
-      assert(x == F(0x0.5p0));
-    }
-    { // negative sign and no leading digit
-      const char* s = "-.5";
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 7);
+    assert(x == F(0x1.5p10));
+  }
+  { // exponent - sign
+    const char* s = "1.5p-10";
 
-      // the expected form of the subject sequence is a nonempty sequence of
-      // decimal digits optionally containing a decimal-point character, then
-      // an optional exponent part as defined in 6.4.4.3, excluding any digit
-      // separators (6.4.4.2); (C23 7.24.1.5)
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 3);
-      assert(x == F(-0x0.5p0));
-    }
-    { // double decimal point
-      const char* s = "1.25.78";
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 7);
+    assert(x == F(0x1.5p-10));
+  }
+  { // Exponent no number
+    const char* s = "1.5p";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 3);
+    assert(x == F(0x1.5p0));
+  }
+  { // Exponent sign no number
+    {
+      const char* s = "1.5p+";
 
-      // This number is halfway between two float values.
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 4);
-      assert(x == F(0x1.25p0));
+      assert(r.ptr == s + 3);
+      assert(x == F(0x1.5p0));
     }
-    { // exponent no sign
-      const char* s = "1.5p10";
+    {
+      const char* s = "1.5p-";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
-      assert(x == F(0x1.5p10));
+      assert(r.ptr == s + 3);
+      assert(x == F(0x1.5p0));
     }
-    { // exponent capitalized no sign
-      const char* s = "1.5P10";
+  }
+  { // Exponent with whitespace
+    {
+      const char* s = "1.5p +1";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
-      assert(x == F(0x1.5p10));
+      assert(r.ptr == s + 3);
+      assert(x == F(0x1.5p0));
     }
-    { // exponent + sign
-      const char* s = "1.5p+10";
+    {
+      const char* s = "1.5p+ 1";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 7);
-      assert(x == F(0x1.5p10));
+      assert(r.ptr == s + 3);
+      assert(x == F(0x1.5p0));
     }
-    { // exponent - sign
-      const char* s = "1.5p-10";
+    {
+      const char* s = "1.5p -1";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 7);
-      assert(x == F(0x1.5p-10));
+      assert(r.ptr == s + 3);
+      assert(x == F(0x1.5p0));
     }
-    { // Exponent no number
-      const char* s = "1.5p";
+    {
+      const char* s = "1.5p- 1";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + 3);
       assert(x == F(0x1.5p0));
     }
-    { // Exponent sign no number
-      {
-        const char* s = "1.5p+";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(0x1.5p0));
-      }
-      {
-        const char* s = "1.5p-";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(0x1.5p0));
-      }
-    }
-    { // Exponent with whitespace
-      {
-        const char* s = "1.5p +1";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(0x1.5p0));
-      }
-      {
-        const char* s = "1.5p+ 1";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(0x1.5p0));
-      }
-      {
-        const char* s = "1.5p -1";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(0x1.5p0));
-      }
-      {
-        const char* s = "1.5p- 1";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 3);
-        assert(x == F(0x1.5p0));
-      }
-    }
-    { // Exponent double sign
-      {
-        const char* s = "1.25p++12";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 4);
-        assert(x == F(0x1.25p0));
-      }
-      {
-        const char* s = "1.25p+-12";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 4);
-        assert(x == F(0x1.25p0));
-      }
-      {
-        const char* s = "1.25p-+12";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 4);
-        assert(x == F(0x1.25p0));
-      }
-      {
-        const char* s = "1.25p--12";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + 4);
-        assert(x == F(0x1.25p0));
-      }
-    }
-    { // exponent hex prefix -> p0
-      const char* s = "1.25p0x12";
+  }
+  { // Exponent double sign
+    {
+      const char* s = "1.25p++12";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
+      assert(r.ptr == s + 4);
       assert(x == F(0x1.25p0));
     }
-    { // double exponent
-      const char* s = "1.25p0p12";
+    {
+      const char* s = "1.25p+-12";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 6);
+      assert(r.ptr == s + 4);
       assert(x == F(0x1.25p0));
     }
-    { // This number is halfway between two float values.
-      const char* s = "131CA25";
+    {
+      const char* s = "1.25p-+12";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 7);
-      assert(x == F(0x131CA25p0));
+      assert(r.ptr == s + 4);
+      assert(x == F(0x1.25p0));
     }
-    { // Shifting mantissa exponent and no exponent
-      const char* s = "123.456";
+    {
+      const char* s = "1.25p--12";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
       assert(r.ec == std::errc{});
-      assert(r.ptr == s + 7);
-      assert(x == F(0x123.456p0));
+      assert(r.ptr == s + 4);
+      assert(x == F(0x1.25p0));
     }
-    { // Shifting mantissa exponent and an exponent
-      const char* s = "123.456p3";
+  }
+  { // exponent hex prefix -> p0
+    const char* s = "1.25p0x12";
 
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-      assert(r.ec == std::errc{});
-      assert(r.ptr == s + 9);
-      assert(x == F(0x123.456p3));
-    }
-    { // Mantissa overflow
-      {
-        const char* s = "0.111111111111111111111111111111111111111111";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + std::strlen(s));
-        assert(x == F(0x0.111111111111111111111111111111111111111111p0));
-      }
-      {
-        const char* s = "111111111111.111111111111111111111111111111111111111111";
-
-        r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-        assert(r.ec == std::errc{});
-        assert(r.ptr == s + std::strlen(s));
-        assert(x == F(0x111111111111.111111111111111111111111111111111111111111p0));
-      }
-    }
-    { // Negative value
-      const char* s = "-0.25";
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(0x1.25p0));
+  }
+  { // double exponent
+    const char* s = "1.25p0p12";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 6);
+    assert(x == F(0x1.25p0));
+  }
+  { // This number is halfway between two float values.
+    const char* s = "131CA25";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 7);
+    assert(x == F(0x131CA25p0));
+  }
+  { // Shifting mantissa exponent and no exponent
+    const char* s = "123.456";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 7);
+    assert(x == F(0x123.456p0));
+  }
+  { // Shifting mantissa exponent and an exponent
+    const char* s = "123.456p3";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + 9);
+    assert(x == F(0x123.456p3));
+  }
+  { // Mantissa overflow
+    {
+      const char* s = "0.111111111111111111111111111111111111111111";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + std::strlen(s));
-      assert(x == F(-0x0.25p0));
+      assert(x == F(0x0.111111111111111111111111111111111111111111p0));
     }
-    { // value is too big -> +inf
-      const char* s = "1p9999999999999999999999999999999999999999";
+    {
+      const char* s = "111111111111.111111111111111111111111111111111111111111";
 
       r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-      assert(r.ec == std::errc::result_out_of_range);
-      assert(r.ptr == s + strlen(s));
-      assert(x == std::numeric_limits<F>::infinity());
+      assert(r.ec == std::errc{});
+      assert(r.ptr == s + std::strlen(s));
+      assert(x == F(0x111111111111.111111111111111111111111111111111111111111p0));
     }
-    { // negative value is too big -> -inf
-      const char* s = "-1p9999999999999999999999999999999999999999";
+  }
+  { // Negative value
+    const char* s = "-0.25";
 
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-      assert(r.ec == std::errc::result_out_of_range);
-      assert(r.ptr == s + strlen(s));
-      assert(x == -std::numeric_limits<F>::infinity());
-    }
-    { // value is too small -> 0
-      const char* s = "1p-9999999999999999999999999999999999999999";
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc{});
+    assert(r.ptr == s + std::strlen(s));
+    assert(x == F(-0x0.25p0));
+  }
+  { // value is too big -> +inf
+    const char* s = "1p9999999999999999999999999999999999999999";
 
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-      assert(r.ec == std::errc::result_out_of_range);
-      assert(r.ptr == s + strlen(s));
-      assert(x == F(0.0));
-    }
-    { // negative value is too small -> -0
-      const char* s = "-1p-9999999999999999999999999999999999999999";
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc::result_out_of_range);
+    assert(r.ptr == s + strlen(s));
+    assert(x == std::numeric_limits<F>::infinity());
+  }
+  { // negative value is too big -> -inf
+    const char* s = "-1p9999999999999999999999999999999999999999";
 
-      r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
-      assert(r.ec == std::errc::result_out_of_range);
-      assert(r.ptr == s + strlen(s));
-      assert(x == F(-0.0));
-    }
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc::result_out_of_range);
+    assert(r.ptr == s + strlen(s));
+    assert(x == -std::numeric_limits<F>::infinity());
   }
-};
+  { // value is too small -> 0
+    const char* s = "1p-9999999999999999999999999999999999999999";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc::result_out_of_range);
+    assert(r.ptr == s + strlen(s));
+    assert(x == F(0.0));
+  }
+  { // negative value is too small -> -0
+    const char* s = "-1p-9999999999999999999999999999999999999999";
+
+    r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+    assert(r.ec == std::errc::result_out_of_range);
+    assert(r.ptr == s + strlen(s));
+    assert(x == F(-0.0));
+  }
+}
 
 // The test
 //   test/std/utilities/charconv/charconv.msvc/test.cpp
@@ -1546,13 +1537,20 @@ void test_random_errors() {
   }
 }
 
-int main(int, char**) {
-  run<test_basics>(all_floats);
-  run<test_scientific>(all_floats);
-  run<test_fixed>(all_floats);
-  run<test_general>(all_floats);
+struct Test {
+  template <class FloatT>
+  void operator()() {
+    test_basics<FloatT>();
+    test_scientific<FloatT>();
+    test_fixed<FloatT>();
+    test_general<FloatT>();
+    test_hex<FloatT>();
+  }
+};
 
-  run<test_hex>(all_floats);
+int main(int, char**) {
+  //TODO: Make this floating_point_types
+  types::for_each(types::type_list<float, double>{}, Test());
 
   test_random_errors();
 
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.bool.verify.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.bool.verify.cpp
deleted file mode 100644
index 5fa62a4683350..0000000000000
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.bool.verify.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// <charconv>
-
-// In
-//
-// from_chars_result from_chars(const char* first, const char* last,
-//                              Integral& value, int base = 10)
-//
-// Integral cannot be bool.
-
-#include <charconv>
-
-int main(int, char**)
-{
-    using std::from_chars;
-    char buf[] = "01001";
-    bool lv;
-
-    from_chars(buf, buf + sizeof(buf), lv);      // expected-error {{call to deleted function}}
-    from_chars(buf, buf + sizeof(buf), lv, 16);  // expected-error {{call to deleted function}}
-
-  return 0;
-}
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp
index 4708da4c38b9b..1e0aaebb1006e 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp
@@ -13,15 +13,31 @@
 // constexpr from_chars_result from_chars(const char* first, const char* last,
 //                                        Integral& value, int base = 10)
 
+#include <cassert>
 #include <charconv>
 #include <system_error>
 
-#include "test_macros.h"
 #include "charconv_test_helpers.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+template <class, class... Args>
+constexpr bool has_from_chars_impl = false;
+
+template <class... Args>
+constexpr bool has_from_chars_impl<decltype((void)std::from_chars(std::declval<Args>()...)), Args...> = true;
+
+template <class... Args>
+constexpr bool has_from_chars = has_from_chars_impl<void, Args...>;
+
+static_assert(has_from_chars<char*, char*, int&>);
+static_assert(has_from_chars<char*, char*, int&, int>);
+static_assert(!has_from_chars<char*, char*, bool&>);
+static_assert(!has_from_chars<char*, char*, bool&, int>);
 
-template <typename T>
 struct test_basics
 {
+    template <typename T>
     TEST_CONSTEXPR_CXX23 void operator()()
     {
         std::from_chars_result r;
@@ -83,9 +99,9 @@ struct test_basics
     }
 };
 
-template <typename T>
 struct test_signed
 {
+    template <typename T>
     TEST_CONSTEXPR_CXX23 void operator()()
     {
         std::from_chars_result r;
@@ -139,10 +155,10 @@ struct test_signed
 
 TEST_CONSTEXPR_CXX23 bool test()
 {
-    run<test_basics>(integrals);
-    run<test_signed>(all_signed);
+  types::for_each(types::integer_types{}, test_basics{});
+  types::for_each(types::signed_integer_types{}, test_signed{});
 
-    return true;
+  return true;
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.roundtrip.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.roundtrip.pass.cpp
index b1f2c77b34f8a..eaf353da16bc6 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.roundtrip.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.roundtrip.pass.cpp
@@ -21,75 +21,101 @@
 #include "test_macros.h"
 #include "charconv_test_helpers.h"
 
-template <typename T>
-struct test_basics : roundtrip_test_base<T>
-{
-    using roundtrip_test_base<T>::test;
-
-    TEST_CONSTEXPR_CXX23 void operator()()
-    {
-        test(0);
-        test(42);
-        test(32768);
-        test(0, 10);
-        test(42, 10);
-        test(32768, 10);
-        test(0xf, 16);
-        test(0xdeadbeaf, 16);
-        test(0755, 8);
-
-        for (int b = 2; b < 37; ++b)
-        {
-            using xl = std::numeric_limits<T>;
-
-            test(1, b);
-            test(-1, b);
-            test(xl::lowest(), b);
-            test((xl::max)(), b);
-            test((xl::max)() / 2, b);
-        }
+template <class X, class T, class... Ts>
+TEST_CONSTEXPR_CXX23 void test_roundtrip(T v, Ts... args) {
+  std::from_chars_result r2;
+  std::to_chars_result r;
+  X x = 0xc;
+  char buf[150];
+
+  if (fits_in<X>(v)) {
+    r = std::to_chars(buf, buf + sizeof(buf), v, args...);
+    assert(r.ec == std::errc{});
+
+    r2 = std::from_chars(buf, r.ptr, x, args...);
+    assert(r2.ptr == r.ptr);
+    assert(x == X(v));
+  } else {
+    r = std::to_chars(buf, buf + sizeof(buf), v, args...);
+    assert(r.ec == std::errc{});
+
+    r2 = std::from_chars(buf, r.ptr, x, args...);
+
+    TEST_DIAGNOSTIC_PUSH
+    TEST_MSVC_DIAGNOSTIC_IGNORED(4127) // conditional expression is constant
+
+    if (std::is_signed<T>::value && v < 0 && std::is_unsigned<X>::value) {
+      assert(x == 0xc);
+      assert(r2.ptr == buf);
+      assert(r2.ec == std::errc::invalid_argument);
+    } else {
+      assert(x == 0xc);
+      assert(r2.ptr == r.ptr);
+      assert(r2.ec == std::errc::result_out_of_range);
     }
+
+    TEST_DIAGNOSTIC_POP
+  }
+}
+
+struct test_basics {
+  template <typename T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test_roundtrip<T>(0);
+    test_roundtrip<T>(42);
+    test_roundtrip<T>(32768);
+    test_roundtrip<T>(0, 10);
+    test_roundtrip<T>(42, 10);
+    test_roundtrip<T>(32768, 10);
+    test_roundtrip<T>(0xf, 16);
+    test_roundtrip<T>(0xdeadbeaf, 16);
+    test_roundtrip<T>(0755, 8);
+
+    for (int b = 2; b < 37; ++b) {
+      using xl = std::numeric_limits<T>;
+
+      test_roundtrip<T>(1, b);
+      test_roundtrip<T>(-1, b);
+      test_roundtrip<T>(xl::lowest(), b);
+      test_roundtrip<T>((xl::max)(), b);
+      test_roundtrip<T>((xl::max)() / 2, b);
+    }
+  }
 };
 
-template <typename T>
-struct test_signed : roundtrip_test_base<T>
-{
-    using roundtrip_test_base<T>::test;
-
-    TEST_CONSTEXPR_CXX23 void operator()()
-    {
-        test(-1);
-        test(-12);
-        test(-1, 10);
-        test(-12, 10);
-        test(-21734634, 10);
-        test(-2647, 2);
-        test(-0xcc1, 16);
-
-        for (int b = 2; b < 37; ++b)
-        {
-            using xl = std::numeric_limits<T>;
-
-            test(0, b);
-            test(xl::lowest(), b);
-            test((xl::max)(), b);
-        }
+struct test_signed {
+  template <typename T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test_roundtrip<T>(-1);
+    test_roundtrip<T>(-12);
+    test_roundtrip<T>(-1, 10);
+    test_roundtrip<T>(-12, 10);
+    test_roundtrip<T>(-21734634, 10);
+    test_roundtrip<T>(-2647, 2);
+    test_roundtrip<T>(-0xcc1, 16);
+
+    for (int b = 2; b < 37; ++b) {
+      using xl = std::numeric_limits<T>;
+
+      test_roundtrip<T>(0, b);
+      test_roundtrip<T>(xl::lowest(), b);
+      test_roundtrip<T>((xl::max)(), b);
     }
+  }
 };
 
-TEST_CONSTEXPR_CXX23 bool test()
-{
-    run<test_basics>(integrals);
-    run<test_signed>(all_signed);
+TEST_CONSTEXPR_CXX23 bool test() {
+  types::for_each(types::integer_types{}, test_basics{});
+  types::for_each(types::signed_integer_types{}, test_signed{});
 
-    return true;
+  return true;
 }
 
 int main(int, char**) {
-    test();
+  test();
 #if TEST_STD_VER > 20
-    static_assert(test());
+  static_assert(test());
 #endif
 
-    return 0;
+  return 0;
 }
diff --git a/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.bool.verify.cpp b/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.bool.verify.cpp
deleted file mode 100644
index 630799b6738a2..0000000000000
--- a/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.bool.verify.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// <charconv>
-
-// In
-//
-// to_chars_result to_chars(char* first, char* last, Integral value,
-//                          int base = 10)
-//
-// Integral cannot be bool.
-
-#include <charconv>
-
-int main(int, char**)
-{
-    using std::to_chars;
-    char buf[10];
-    bool lv = true;
-
-    to_chars(buf, buf + sizeof(buf), false);   // expected-error {{call to deleted function}}
-    to_chars(buf, buf + sizeof(buf), lv, 16);  // expected-error {{call to deleted function}}
-
-  return 0;
-}
diff --git a/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp
index e89b340bdfcbe..2dcefd66396d6 100644
--- a/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp
@@ -17,12 +17,161 @@
 // constexpr to_chars_result to_chars(char* first, char* last, Integral value,
 //                                    int base = 10)
 
+#include <algorithm>
+#include <cassert>
 #include <charconv>
 #include <cstdint>
+#include <cstdlib>
+#include <numeric>
 #include <system_error>
 
-#include "test_macros.h"
 #include "charconv_test_helpers.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+template <class, class... Args>
+constexpr bool has_to_chars_impl = false;
+
+template <class... Args>
+constexpr bool has_to_chars_impl<decltype((void)std::to_chars(std::declval<Args>()...)), Args...> = true;
+
+template <class... Args>
+constexpr bool has_to_chars = has_to_chars_impl<void, Args...>;
+
+static_assert(has_to_chars<char*, char*, int&>);
+static_assert(has_to_chars<char*, char*, int&, int>);
+static_assert(!has_to_chars<char*, char*, bool&>);
+static_assert(!has_to_chars<char*, char*, bool&, int>);
+
+template <class X, class T, std::size_t N, class... Ts>
+TEST_CONSTEXPR_CXX23 void test(T v, char const (&expect)[N], Ts... args) {
+  std::to_chars_result r;
+  char buf[150];
+
+  constexpr std::size_t len = N - 1;
+  static_assert(len > 0, "expected output won't be empty");
+
+  if (!fits_in<X>(v))
+    return;
+
+  r = std::to_chars(buf, buf + len - 1, X(v), args...);
+  assert(r.ptr == buf + len - 1);
+  assert(r.ec == std::errc::value_too_large);
+
+  r = std::to_chars(buf, buf + sizeof(buf), X(v), args...);
+  assert(r.ptr == buf + len);
+  assert(r.ec == std::errc{});
+  assert(std::equal(buf, buf + len, expect));
+}
+
+TEST_CONSTEXPR_CXX23 long long fromchars_impl(char const* p, char const* ep, int base, std::true_type) {
+  char* last;
+  long long r;
+  if (TEST_IS_CONSTANT_EVALUATED)
+    last = const_cast<char*>(std::from_chars(p, ep, r, base).ptr);
+  else
+    r = strtoll(p, &last, base);
+  assert(last == ep);
+
+  return r;
+}
+
+TEST_CONSTEXPR_CXX23 unsigned long long fromchars_impl(char const* p, char const* ep, int base, std::false_type) {
+  char* last;
+  unsigned long long r;
+  if (TEST_IS_CONSTANT_EVALUATED)
+    last = const_cast<char*>(std::from_chars(p, ep, r, base).ptr);
+  else
+    r = strtoull(p, &last, base);
+  assert(last == ep);
+
+  return r;
+}
+#ifndef TEST_HAS_NO_INT128
+TEST_CONSTEXPR_CXX23 __int128_t fromchars128_impl(char const* p, char const* ep, int base, std::true_type) {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    char* last;
+    __int128_t r = strtoll(p, &last, base);
+    if (errno != ERANGE) {
+      assert(last == ep);
+      return r;
+    }
+  }
+
+  // When the value doesn't fit in a long long use from_chars. This is
+  // not ideal since it does a round-trip test instead if using an
+  // external source.
+  __int128_t r;
+  std::from_chars_result s = std::from_chars(p, ep, r, base);
+  assert(s.ec == std::errc{});
+  assert(s.ptr == ep);
+
+  return r;
+}
+
+TEST_CONSTEXPR_CXX23 __uint128_t fromchars128_impl(char const* p, char const* ep, int base, std::false_type) {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    char* last;
+    __uint128_t r = strtoull(p, &last, base);
+    if (errno != ERANGE) {
+      assert(last == ep);
+      return r;
+    }
+  }
+
+  __uint128_t r;
+  std::from_chars_result s = std::from_chars(p, ep, r, base);
+  assert(s.ec == std::errc{});
+  assert(s.ptr == ep);
+
+  return r;
+}
+
+template <class X>
+TEST_CONSTEXPR_CXX23 auto fromchars128_impl(char const* p, char const* ep, int base = 10)
+    -> decltype(fromchars128_impl(p, ep, base, std::is_signed<X>())) {
+  return fromchars128_impl(p, ep, base, std::is_signed<X>());
+}
+
+#endif
+
+template <class X>
+TEST_CONSTEXPR_CXX23 auto fromchars_impl(char const* p, char const* ep, int base = 10)
+    -> decltype(fromchars_impl(p, ep, base, std::is_signed<X>())) {
+  return fromchars_impl(p, ep, base, std::is_signed<X>());
+}
+
+template <class X, class... Ts>
+TEST_CONSTEXPR_CXX23 void test_value(X v, Ts... args) {
+  std::to_chars_result r;
+  char buf[150];
+
+  // Poison the buffer for testing whether a successful std::to_chars
+  // doesn't modify data beyond r.ptr. Use unsigned values to avoid
+  // overflowing char when it's signed.
+  std::iota(buf, buf + sizeof(buf), static_cast<unsigned char>(1));
+  r = std::to_chars(buf, buf + sizeof(buf), v, args...);
+  assert(r.ec == std::errc{});
+  for (std::size_t i = r.ptr - buf; i < sizeof(buf); ++i)
+    assert(static_cast<unsigned char>(buf[i]) == i + 1);
+  *r.ptr = '\0';
+
+#ifndef TEST_HAS_NO_INT128
+  if (sizeof(X) == sizeof(__int128_t)) {
+    auto a = fromchars128_impl<X>(buf, r.ptr, args...);
+    assert(v == a);
+  } else
+#endif
+  {
+    auto a = fromchars_impl<X>(buf, r.ptr, args...);
+    assert(v == a);
+  }
+
+  auto ep = r.ptr - 1;
+  r       = std::to_chars(buf, ep, v, args...);
+  assert(r.ptr == ep);
+  assert(r.ec == std::errc::value_too_large);
+}
 
 #ifndef TEST_HAS_NO_INT128
 TEST_CONSTEXPR_CXX23 __uint128_t make_u128(__uint128_t a, std::uint64_t b) {
@@ -51,261 +200,247 @@ TEST_CONSTEXPR_CXX23 __int128_t make_i128(__int128_t a, __int128_t b, std::int64
 }
 #endif
 
-template <typename T>
-struct test_basics : to_chars_test_base<T>
-{
-    using to_chars_test_base<T>::test;
-    using to_chars_test_base<T>::test_value;
-
-    TEST_CONSTEXPR_CXX23 void operator()()
-    {
-        test(0, "0");
-        test(42, "42");
-        test(32768, "32768");
-        test(0, "0", 10);
-        test(42, "42", 10);
-        test(32768, "32768", 10);
-        test(0xf, "f", 16);
-        test(0xdeadbeaf, "deadbeaf", 16);
-        test(0755, "755", 8);
-
-        // Test each len till len of UINT64_MAX = 20 because to_chars algorithm
-        // makes branches based on decimal digits count in the value string
-        // representation.
-        // Test driver automatically skips values not fitting into source type.
-        test(1UL, "1");
-        test(12UL, "12");
-        test(123UL, "123");
-        test(1234UL, "1234");
-        test(12345UL, "12345");
-        test(123456UL, "123456");
-        test(1234567UL, "1234567");
-        test(12345678UL, "12345678");
-        test(123456789UL, "123456789");
-        test(1234567890UL, "1234567890");
-        test(12345678901UL, "12345678901");
-        test(123456789012UL, "123456789012");
-        test(1234567890123UL, "1234567890123");
-        test(12345678901234UL, "12345678901234");
-        test(123456789012345UL, "123456789012345");
-        test(1234567890123456UL, "1234567890123456");
-        test(12345678901234567UL, "12345678901234567");
-        test(123456789012345678UL, "123456789012345678");
-        test(1234567890123456789UL, "1234567890123456789");
-        test(12345678901234567890UL, "12345678901234567890");
+struct test_basics {
+  template <typename T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>(0, "0");
+    test<T>(42, "42");
+    test<T>(32768, "32768");
+    test<T>(0, "0", 10);
+    test<T>(42, "42", 10);
+    test<T>(32768, "32768", 10);
+    test<T>(0xf, "f", 16);
+    test<T>(0xdeadbeaf, "deadbeaf", 16);
+    test<T>(0755, "755", 8);
+
+    // Test each len till len of UINT64_MAX = 20 because to_chars algorithm
+    // makes branches based on decimal digits count in the value string
+    // representation.
+    // Test driver automatically skips values not fitting into source type.
+    test<T>(1UL, "1");
+    test<T>(12UL, "12");
+    test<T>(123UL, "123");
+    test<T>(1234UL, "1234");
+    test<T>(12345UL, "12345");
+    test<T>(123456UL, "123456");
+    test<T>(1234567UL, "1234567");
+    test<T>(12345678UL, "12345678");
+    test<T>(123456789UL, "123456789");
+    test<T>(1234567890UL, "1234567890");
+    test<T>(12345678901UL, "12345678901");
+    test<T>(123456789012UL, "123456789012");
+    test<T>(1234567890123UL, "1234567890123");
+    test<T>(12345678901234UL, "12345678901234");
+    test<T>(123456789012345UL, "123456789012345");
+    test<T>(1234567890123456UL, "1234567890123456");
+    test<T>(12345678901234567UL, "12345678901234567");
+    test<T>(123456789012345678UL, "123456789012345678");
+    test<T>(1234567890123456789UL, "1234567890123456789");
+    test<T>(12345678901234567890UL, "12345678901234567890");
 #ifndef TEST_HAS_NO_INT128
-        test(make_u128(12UL, 3456789012345678901UL), "123456789012345678901");
-        test(make_u128(123UL, 4567890123456789012UL), "1234567890123456789012");
-        test(make_u128(1234UL, 5678901234567890123UL), "12345678901234567890123");
-        test(make_u128(12345UL, 6789012345678901234UL), "123456789012345678901234");
-        test(make_u128(123456UL, 7890123456789012345UL), "1234567890123456789012345");
-        test(make_u128(1234567UL, 8901234567890123456UL), "12345678901234567890123456");
-        test(make_u128(12345678UL, 9012345678901234567UL), "123456789012345678901234567");
-        test(make_u128(123456789UL, 123456789012345678UL), "1234567890123456789012345678");
-        test(make_u128(123UL, 4567890123456UL, 7890123456789UL), "12345678901234567890123456789");
-        test(make_u128(1234UL, 5678901234567UL, 8901234567890UL), "123456789012345678901234567890");
-        test(make_u128(12345UL, 6789012345678UL, 9012345678901UL), "1234567890123456789012345678901");
-        test(make_u128(123456UL, 7890123456789UL, 123456789012UL), "12345678901234567890123456789012");
-        test(make_u128(1234567UL, 8901234567890UL, 1234567890123UL), "123456789012345678901234567890123");
-        test(make_u128(12345678UL, 9012345678901UL, 2345678901234UL), "1234567890123456789012345678901234");
-        test(make_u128(123456789UL, 123456789012UL, 3456789012345UL), "12345678901234567890123456789012345");
-        test(make_u128(1234567890UL, 1234567890123UL, 4567890123456UL), "123456789012345678901234567890123456");
-        test(make_u128(12345678901UL, 2345678901234UL, 5678901234567UL), "1234567890123456789012345678901234567");
-        test(make_u128(123456789012UL, 3456789012345UL, 6789012345678UL), "12345678901234567890123456789012345678");
-        test(make_u128(1234567890123UL, 4567890123456UL, 7890123456789UL), "123456789012345678901234567890123456789");
+    test<T>(make_u128(12UL, 3456789012345678901UL), "123456789012345678901");
+    test<T>(make_u128(123UL, 4567890123456789012UL), "1234567890123456789012");
+    test<T>(make_u128(1234UL, 5678901234567890123UL), "12345678901234567890123");
+    test<T>(make_u128(12345UL, 6789012345678901234UL), "123456789012345678901234");
+    test<T>(make_u128(123456UL, 7890123456789012345UL), "1234567890123456789012345");
+    test<T>(make_u128(1234567UL, 8901234567890123456UL), "12345678901234567890123456");
+    test<T>(make_u128(12345678UL, 9012345678901234567UL), "123456789012345678901234567");
+    test<T>(make_u128(123456789UL, 123456789012345678UL), "1234567890123456789012345678");
+    test<T>(make_u128(123UL, 4567890123456UL, 7890123456789UL), "12345678901234567890123456789");
+    test<T>(make_u128(1234UL, 5678901234567UL, 8901234567890UL), "123456789012345678901234567890");
+    test<T>(make_u128(12345UL, 6789012345678UL, 9012345678901UL), "1234567890123456789012345678901");
+    test<T>(make_u128(123456UL, 7890123456789UL, 123456789012UL), "12345678901234567890123456789012");
+    test<T>(make_u128(1234567UL, 8901234567890UL, 1234567890123UL), "123456789012345678901234567890123");
+    test<T>(make_u128(12345678UL, 9012345678901UL, 2345678901234UL), "1234567890123456789012345678901234");
+    test<T>(make_u128(123456789UL, 123456789012UL, 3456789012345UL), "12345678901234567890123456789012345");
+    test<T>(make_u128(1234567890UL, 1234567890123UL, 4567890123456UL), "123456789012345678901234567890123456");
+    test<T>(make_u128(12345678901UL, 2345678901234UL, 5678901234567UL), "1234567890123456789012345678901234567");
+    test<T>(make_u128(123456789012UL, 3456789012345UL, 6789012345678UL), "12345678901234567890123456789012345678");
+    test<T>(make_u128(1234567890123UL, 4567890123456UL, 7890123456789UL), "123456789012345678901234567890123456789");
 #endif
 
-        // Test special cases with zeros inside a value string representation,
-        // to_chars algorithm processes them in a special way and should not
-        // skip trailing zeros
-        // Test driver automatically skips values not fitting into source type.
-        test(0UL, "0");
-        test(10UL, "10");
-        test(100UL, "100");
-        test(1000UL, "1000");
-        test(10000UL, "10000");
-        test(100000UL, "100000");
-        test(1000000UL, "1000000");
-        test(10000000UL, "10000000");
-        test(100000000UL, "100000000");
-        test(1000000000UL, "1000000000");
-        test(10000000000UL, "10000000000");
-        test(100000000000UL, "100000000000");
-        test(1000000000000UL, "1000000000000");
-        test(10000000000000UL, "10000000000000");
-        test(100000000000000UL, "100000000000000");
-        test(1000000000000000UL, "1000000000000000");
-        test(10000000000000000UL, "10000000000000000");
-        test(100000000000000000UL, "100000000000000000");
-        test(1000000000000000000UL, "1000000000000000000");
-        test(10000000000000000000UL, "10000000000000000000");
+    // Test special cases with zeros inside a value string representation,
+    // to_chars algorithm processes them in a special way and should not
+    // skip trailing zeros
+    // Test driver automatically skips values not fitting into source type.
+    test<T>(0UL, "0");
+    test<T>(10UL, "10");
+    test<T>(100UL, "100");
+    test<T>(1000UL, "1000");
+    test<T>(10000UL, "10000");
+    test<T>(100000UL, "100000");
+    test<T>(1000000UL, "1000000");
+    test<T>(10000000UL, "10000000");
+    test<T>(100000000UL, "100000000");
+    test<T>(1000000000UL, "1000000000");
+    test<T>(10000000000UL, "10000000000");
+    test<T>(100000000000UL, "100000000000");
+    test<T>(1000000000000UL, "1000000000000");
+    test<T>(10000000000000UL, "10000000000000");
+    test<T>(100000000000000UL, "100000000000000");
+    test<T>(1000000000000000UL, "1000000000000000");
+    test<T>(10000000000000000UL, "10000000000000000");
+    test<T>(100000000000000000UL, "100000000000000000");
+    test<T>(1000000000000000000UL, "1000000000000000000");
+    test<T>(10000000000000000000UL, "10000000000000000000");
 #ifndef TEST_HAS_NO_INT128
-        test(make_u128(10UL, 0), "100000000000000000000");
-        test(make_u128(100UL, 0), "1000000000000000000000");
-        test(make_u128(1000UL, 0), "10000000000000000000000");
-        test(make_u128(10000UL, 0), "100000000000000000000000");
-        test(make_u128(100000UL, 0), "1000000000000000000000000");
-        test(make_u128(1000000UL, 0), "10000000000000000000000000");
-        test(make_u128(10000000UL, 0), "100000000000000000000000000");
-        test(make_u128(100000000UL, 0), "1000000000000000000000000000");
-        test(make_u128(100UL, 0, 0), "10000000000000000000000000000");
-        test(make_u128(1000UL, 0, 0), "100000000000000000000000000000");
-        test(make_u128(10000UL, 0, 0), "1000000000000000000000000000000");
-        test(make_u128(100000UL, 0, 0), "10000000000000000000000000000000");
-        test(make_u128(1000000UL, 0, 0), "100000000000000000000000000000000");
-        test(make_u128(10000000UL, 0, 0), "1000000000000000000000000000000000");
-        test(make_u128(100000000UL, 0, 0), "10000000000000000000000000000000000");
-        test(make_u128(1000000000UL, 0, 0), "100000000000000000000000000000000000");
-        test(make_u128(10000000000UL, 0, 0), "1000000000000000000000000000000000000");
-        test(make_u128(100000000000UL, 0, 0), "10000000000000000000000000000000000000");
-        test(make_u128(1000000000000UL, 0, 0), "100000000000000000000000000000000000000");
+    test<T>(make_u128(10UL, 0), "100000000000000000000");
+    test<T>(make_u128(100UL, 0), "1000000000000000000000");
+    test<T>(make_u128(1000UL, 0), "10000000000000000000000");
+    test<T>(make_u128(10000UL, 0), "100000000000000000000000");
+    test<T>(make_u128(100000UL, 0), "1000000000000000000000000");
+    test<T>(make_u128(1000000UL, 0), "10000000000000000000000000");
+    test<T>(make_u128(10000000UL, 0), "100000000000000000000000000");
+    test<T>(make_u128(100000000UL, 0), "1000000000000000000000000000");
+    test<T>(make_u128(100UL, 0, 0), "10000000000000000000000000000");
+    test<T>(make_u128(1000UL, 0, 0), "100000000000000000000000000000");
+    test<T>(make_u128(10000UL, 0, 0), "1000000000000000000000000000000");
+    test<T>(make_u128(100000UL, 0, 0), "10000000000000000000000000000000");
+    test<T>(make_u128(1000000UL, 0, 0), "100000000000000000000000000000000");
+    test<T>(make_u128(10000000UL, 0, 0), "1000000000000000000000000000000000");
+    test<T>(make_u128(100000000UL, 0, 0), "10000000000000000000000000000000000");
+    test<T>(make_u128(1000000000UL, 0, 0), "100000000000000000000000000000000000");
+    test<T>(make_u128(10000000000UL, 0, 0), "1000000000000000000000000000000000000");
+    test<T>(make_u128(100000000000UL, 0, 0), "10000000000000000000000000000000000000");
+    test<T>(make_u128(1000000000000UL, 0, 0), "100000000000000000000000000000000000000");
 #endif
 
-        for (int b = 2; b < 37; ++b)
-        {
-            using xl = std::numeric_limits<T>;
+    for (int b = 2; b < 37; ++b) {
+      using xl = std::numeric_limits<T>;
 
-            test_value(1, b);
-            test_value(xl::lowest(), b);
-            test_value((xl::max)(), b);
-            test_value((xl::max)() / 2, b);
-        }
+      test_value(1, b);
+      test_value(xl::lowest(), b);
+      test_value((xl::max)(), b);
+      test_value((xl::max)() / 2, b);
     }
+  }
 };
 
-template <typename T>
-struct test_signed : to_chars_test_base<T>
-{
-    using to_chars_test_base<T>::test;
-    using to_chars_test_base<T>::test_value;
-
-    TEST_CONSTEXPR_CXX23 void operator()()
-    {
-        test(-1, "-1");
-        test(-12, "-12");
-        test(-1, "-1", 10);
-        test(-12, "-12", 10);
-        test(-21734634, "-21734634", 10);
-        test(-2647, "-101001010111", 2);
-        test(-0xcc1, "-cc1", 16);
-
-        // Test each len till len of INT64_MAX = 19 because to_chars algorithm
-        // makes branches based on decimal digits count in the value string
-        // representation.
-        // Test driver automatically skips values not fitting into source type.
-        test(-1L, "-1");
-        test(-12L, "-12");
-        test(-123L, "-123");
-        test(-1234L, "-1234");
-        test(-12345L, "-12345");
-        test(-123456L, "-123456");
-        test(-1234567L, "-1234567");
-        test(-12345678L, "-12345678");
-        test(-123456789L, "-123456789");
-        test(-1234567890L, "-1234567890");
-        test(-12345678901L, "-12345678901");
-        test(-123456789012L, "-123456789012");
-        test(-1234567890123L, "-1234567890123");
-        test(-12345678901234L, "-12345678901234");
-        test(-123456789012345L, "-123456789012345");
-        test(-1234567890123456L, "-1234567890123456");
-        test(-12345678901234567L, "-12345678901234567");
-        test(-123456789012345678L, "-123456789012345678");
-        test(-1234567890123456789L, "-1234567890123456789");
+struct test_signed {
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>(-1, "-1");
+    test<T>(-12, "-12");
+    test<T>(-1, "-1", 10);
+    test<T>(-12, "-12", 10);
+    test<T>(-21734634, "-21734634", 10);
+    test<T>(-2647, "-101001010111", 2);
+    test<T>(-0xcc1, "-cc1", 16);
+
+    // Test each len till len of INT64_MAX = 19 because to_chars algorithm
+    // makes branches based on decimal digits count in the value string
+    // representation.
+    // Test driver automatically skips values not fitting into source type.
+    test<T>(-1L, "-1");
+    test<T>(-12L, "-12");
+    test<T>(-123L, "-123");
+    test<T>(-1234L, "-1234");
+    test<T>(-12345L, "-12345");
+    test<T>(-123456L, "-123456");
+    test<T>(-1234567L, "-1234567");
+    test<T>(-12345678L, "-12345678");
+    test<T>(-123456789L, "-123456789");
+    test<T>(-1234567890L, "-1234567890");
+    test<T>(-12345678901L, "-12345678901");
+    test<T>(-123456789012L, "-123456789012");
+    test<T>(-1234567890123L, "-1234567890123");
+    test<T>(-12345678901234L, "-12345678901234");
+    test<T>(-123456789012345L, "-123456789012345");
+    test<T>(-1234567890123456L, "-1234567890123456");
+    test<T>(-12345678901234567L, "-12345678901234567");
+    test<T>(-123456789012345678L, "-123456789012345678");
+    test<T>(-1234567890123456789L, "-1234567890123456789");
 #ifndef TEST_HAS_NO_INT128
-        test(make_i128(-1L, 2345678901234567890L), "-12345678901234567890");
-        test(make_i128(-12L, 3456789012345678901L), "-123456789012345678901");
-        test(make_i128(-123L, 4567890123456789012L), "-1234567890123456789012");
-        test(make_i128(-1234L, 5678901234567890123L), "-12345678901234567890123");
-        test(make_i128(-12345L, 6789012345678901234L), "-123456789012345678901234");
-        test(make_i128(-123456L, 7890123456789012345L), "-1234567890123456789012345");
-        test(make_i128(-1234567L, 8901234567890123456L), "-12345678901234567890123456");
-        test(make_i128(-12345678L, 9012345678901234567L), "-123456789012345678901234567");
-        test(make_i128(-123456789L, 123456789012345678L), "-1234567890123456789012345678");
-        test(make_i128(-1234567890L, 1234567890123456789L), "-12345678901234567890123456789");
-        test(make_i128(-123L, 4567890123456L, 7890123456789L), "-12345678901234567890123456789");
-        test(make_i128(-1234L, 5678901234567L, 8901234567890L), "-123456789012345678901234567890");
-        test(make_i128(-12345L, 6789012345678L, 9012345678901L), "-1234567890123456789012345678901");
-        test(make_i128(-123456L, 7890123456789L, 123456789012L), "-12345678901234567890123456789012");
-        test(make_i128(-1234567L, 8901234567890L, 1234567890123L), "-123456789012345678901234567890123");
-        test(make_i128(-12345678L, 9012345678901L, 2345678901234L), "-1234567890123456789012345678901234");
-        test(make_i128(-123456789L, 123456789012L, 3456789012345L), "-12345678901234567890123456789012345");
-        test(make_i128(-1234567890L, 1234567890123L, 4567890123456L), "-123456789012345678901234567890123456");
-        test(make_i128(-12345678901L, 2345678901234L, 5678901234567L), "-1234567890123456789012345678901234567");
-        test(make_i128(-123456789012L, 3456789012345L, 6789012345678L), "-12345678901234567890123456789012345678");
-        test(make_i128(-1234567890123L, 4567890123456L, 7890123456789L), "-123456789012345678901234567890123456789");
+    test<T>(make_i128(-1L, 2345678901234567890L), "-12345678901234567890");
+    test<T>(make_i128(-12L, 3456789012345678901L), "-123456789012345678901");
+    test<T>(make_i128(-123L, 4567890123456789012L), "-1234567890123456789012");
+    test<T>(make_i128(-1234L, 5678901234567890123L), "-12345678901234567890123");
+    test<T>(make_i128(-12345L, 6789012345678901234L), "-123456789012345678901234");
+    test<T>(make_i128(-123456L, 7890123456789012345L), "-1234567890123456789012345");
+    test<T>(make_i128(-1234567L, 8901234567890123456L), "-12345678901234567890123456");
+    test<T>(make_i128(-12345678L, 9012345678901234567L), "-123456789012345678901234567");
+    test<T>(make_i128(-123456789L, 123456789012345678L), "-1234567890123456789012345678");
+    test<T>(make_i128(-1234567890L, 1234567890123456789L), "-12345678901234567890123456789");
+    test<T>(make_i128(-123L, 4567890123456L, 7890123456789L), "-12345678901234567890123456789");
+    test<T>(make_i128(-1234L, 5678901234567L, 8901234567890L), "-123456789012345678901234567890");
+    test<T>(make_i128(-12345L, 6789012345678L, 9012345678901L), "-1234567890123456789012345678901");
+    test<T>(make_i128(-123456L, 7890123456789L, 123456789012L), "-12345678901234567890123456789012");
+    test<T>(make_i128(-1234567L, 8901234567890L, 1234567890123L), "-123456789012345678901234567890123");
+    test<T>(make_i128(-12345678L, 9012345678901L, 2345678901234L), "-1234567890123456789012345678901234");
+    test<T>(make_i128(-123456789L, 123456789012L, 3456789012345L), "-12345678901234567890123456789012345");
+    test<T>(make_i128(-1234567890L, 1234567890123L, 4567890123456L), "-123456789012345678901234567890123456");
+    test<T>(make_i128(-12345678901L, 2345678901234L, 5678901234567L), "-1234567890123456789012345678901234567");
+    test<T>(make_i128(-123456789012L, 3456789012345L, 6789012345678L), "-12345678901234567890123456789012345678");
+    test<T>(make_i128(-1234567890123L, 4567890123456L, 7890123456789L), "-123456789012345678901234567890123456789");
 #endif
 
-        // Test special cases with zeros inside a value string representation,
-        // to_chars algorithm processes them in a special way and should not
-        // skip trailing zeros
-        // Test driver automatically skips values not fitting into source type.
-        test(-10L, "-10");
-        test(-100L, "-100");
-        test(-1000L, "-1000");
-        test(-10000L, "-10000");
-        test(-100000L, "-100000");
-        test(-1000000L, "-1000000");
-        test(-10000000L, "-10000000");
-        test(-100000000L, "-100000000");
-        test(-1000000000L, "-1000000000");
-        test(-10000000000L, "-10000000000");
-        test(-100000000000L, "-100000000000");
-        test(-1000000000000L, "-1000000000000");
-        test(-10000000000000L, "-10000000000000");
-        test(-100000000000000L, "-100000000000000");
-        test(-1000000000000000L, "-1000000000000000");
-        test(-10000000000000000L, "-10000000000000000");
-        test(-100000000000000000L, "-100000000000000000");
-        test(-1000000000000000000L, "-1000000000000000000");
+    // Test special cases with zeros inside a value string representation,
+    // to_chars algorithm processes them in a special way and should not
+    // skip trailing zeros
+    // Test driver automatically skips values not fitting into source type.
+    test<T>(-10L, "-10");
+    test<T>(-100L, "-100");
+    test<T>(-1000L, "-1000");
+    test<T>(-10000L, "-10000");
+    test<T>(-100000L, "-100000");
+    test<T>(-1000000L, "-1000000");
+    test<T>(-10000000L, "-10000000");
+    test<T>(-100000000L, "-100000000");
+    test<T>(-1000000000L, "-1000000000");
+    test<T>(-10000000000L, "-10000000000");
+    test<T>(-100000000000L, "-100000000000");
+    test<T>(-1000000000000L, "-1000000000000");
+    test<T>(-10000000000000L, "-10000000000000");
+    test<T>(-100000000000000L, "-100000000000000");
+    test<T>(-1000000000000000L, "-1000000000000000");
+    test<T>(-10000000000000000L, "-10000000000000000");
+    test<T>(-100000000000000000L, "-100000000000000000");
+    test<T>(-1000000000000000000L, "-1000000000000000000");
 #ifndef TEST_HAS_NO_INT128
-        test(make_i128(-1L, 0L), "-10000000000000000000");
-        test(make_i128(-10L, 0L), "-100000000000000000000");
-        test(make_i128(-100L, 0L), "-1000000000000000000000");
-        test(make_i128(-1000L, 0L), "-10000000000000000000000");
-        test(make_i128(-10000L, 0L), "-100000000000000000000000");
-        test(make_i128(-100000L, 0L), "-1000000000000000000000000");
-        test(make_i128(-1000000L, 0L), "-10000000000000000000000000");
-        test(make_i128(-10000000L, 0L), "-100000000000000000000000000");
-        test(make_i128(-100000000L, 0L), "-1000000000000000000000000000");
-        test(make_i128(-1000000000L, 0L), "-10000000000000000000000000000");
-        test(make_i128(-100L, 0L, 0L), "-10000000000000000000000000000");
-        test(make_i128(-1000L, 0L, 0L), "-100000000000000000000000000000");
-        test(make_i128(-10000L, 0L, 0L), "-1000000000000000000000000000000");
-        test(make_i128(-100000L, 0L, 0L), "-10000000000000000000000000000000");
-        test(make_i128(-1000000L, 0L, 0L), "-100000000000000000000000000000000");
-        test(make_i128(-10000000L, 0L, 0L), "-1000000000000000000000000000000000");
-        test(make_i128(-100000000L, 0L, 0L), "-10000000000000000000000000000000000");
-        test(make_i128(-1000000000L, 0L, 0L), "-100000000000000000000000000000000000");
-        test(make_i128(-10000000000L, 0L, 0L), "-1000000000000000000000000000000000000");
-        test(make_i128(-100000000000L, 0L, 0L), "-10000000000000000000000000000000000000");
-        test(make_i128(-1000000000000L, 0L, 0L), "-100000000000000000000000000000000000000");
+    test<T>(make_i128(-1L, 0L), "-10000000000000000000");
+    test<T>(make_i128(-10L, 0L), "-100000000000000000000");
+    test<T>(make_i128(-100L, 0L), "-1000000000000000000000");
+    test<T>(make_i128(-1000L, 0L), "-10000000000000000000000");
+    test<T>(make_i128(-10000L, 0L), "-100000000000000000000000");
+    test<T>(make_i128(-100000L, 0L), "-1000000000000000000000000");
+    test<T>(make_i128(-1000000L, 0L), "-10000000000000000000000000");
+    test<T>(make_i128(-10000000L, 0L), "-100000000000000000000000000");
+    test<T>(make_i128(-100000000L, 0L), "-1000000000000000000000000000");
+    test<T>(make_i128(-1000000000L, 0L), "-10000000000000000000000000000");
+    test<T>(make_i128(-100L, 0L, 0L), "-10000000000000000000000000000");
+    test<T>(make_i128(-1000L, 0L, 0L), "-100000000000000000000000000000");
+    test<T>(make_i128(-10000L, 0L, 0L), "-1000000000000000000000000000000");
+    test<T>(make_i128(-100000L, 0L, 0L), "-10000000000000000000000000000000");
+    test<T>(make_i128(-1000000L, 0L, 0L), "-100000000000000000000000000000000");
+    test<T>(make_i128(-10000000L, 0L, 0L), "-1000000000000000000000000000000000");
+    test<T>(make_i128(-100000000L, 0L, 0L), "-10000000000000000000000000000000000");
+    test<T>(make_i128(-1000000000L, 0L, 0L), "-100000000000000000000000000000000000");
+    test<T>(make_i128(-10000000000L, 0L, 0L), "-1000000000000000000000000000000000000");
+    test<T>(make_i128(-100000000000L, 0L, 0L), "-10000000000000000000000000000000000000");
+    test<T>(make_i128(-1000000000000L, 0L, 0L), "-100000000000000000000000000000000000000");
 #endif
 
-        for (int b = 2; b < 37; ++b)
-        {
-            using xl = std::numeric_limits<T>;
+    for (int b = 2; b < 37; ++b) {
+      using xl = std::numeric_limits<T>;
 
-            test_value(0, b);
-            test_value(xl::lowest(), b);
-            test_value((xl::max)(), b);
-        }
+      test_value(0, b);
+      test_value(xl::lowest(), b);
+      test_value((xl::max)(), b);
     }
+  }
 };
 
-TEST_CONSTEXPR_CXX23 bool test()
-{
-    run<test_basics>(integrals);
-    run<test_signed>(all_signed);
+TEST_CONSTEXPR_CXX23 bool test() {
+  types::for_each(types::integer_types{}, test_basics{});
+  types::for_each(types::signed_integer_types{}, test_signed{});
 
-    return true;
+  return true;
 }
 
-int main(int, char**)
-{
-    test();
+int main(int, char**) {
+  test();
 #if TEST_STD_VER > 20
-    static_assert(test());
+  static_assert(test());
 #endif
 
   return 0;
diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index fcae09478457b..51c579e7923ad 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -9,15 +9,8 @@
 #ifndef SUPPORT_CHARCONV_TEST_HELPERS_H
 #define SUPPORT_CHARCONV_TEST_HELPERS_H
 
-#include <algorithm>
-#include <cassert>
-#include <cerrno>
-#include <charconv>
 #include <cstddef>
 #include <limits>
-#include <numeric>
-#include <string.h>
-#include <stdlib.h>
 #include <type_traits>
 
 #include "test_macros.h"
@@ -26,47 +19,44 @@
 #error This file requires C++11
 #endif
 
-using std::false_type;
-using std::true_type;
-
 template <typename To, typename From>
 constexpr auto
-is_non_narrowing(From a) -> decltype(To{a}, true_type())
+is_non_narrowing(From a) -> decltype(To{a}, std::true_type())
 {
     return {};
 }
 
 template <typename To>
 constexpr auto
-is_non_narrowing(...) -> false_type
+is_non_narrowing(...) -> std::false_type
 {
     return {};
 }
 
 template <typename X, typename T>
 constexpr bool
-_fits_in(T, true_type /* non-narrowing*/, ...)
+_fits_in(T, std::true_type /* non-narrowing*/, ...)
 {
     return true;
 }
 
 template <typename X, typename T, typename xl = std::numeric_limits<X>>
 constexpr bool
-_fits_in(T v, false_type, true_type /* T signed*/, true_type /* X signed */)
+_fits_in(T v, std::false_type, std::true_type /* T signed*/, std::true_type /* X signed */)
 {
     return xl::lowest() <= v && v <= (xl::max)();
 }
 
 template <typename X, typename T, typename xl = std::numeric_limits<X>>
 constexpr bool
-_fits_in(T v, false_type, true_type /* T signed */, false_type /* X unsigned*/)
+_fits_in(T v, std::false_type, std::true_type /* T signed */, std::false_type /* X unsigned*/)
 {
     return 0 <= v && typename std::make_unsigned<T>::type(v) <= (xl::max)();
 }
 
 template <typename X, typename T, typename xl = std::numeric_limits<X>>
 constexpr bool
-_fits_in(T v, false_type, false_type /* T unsigned */, ...)
+_fits_in(T v, std::false_type, std::false_type /* T unsigned */, ...)
 {
     return v <= typename std::make_unsigned<X>::type((xl::max)());
 }
@@ -78,253 +68,4 @@ fits_in(T v)
   return _fits_in<X>(v, is_non_narrowing<X>(v), std::is_signed<T>(), std::is_signed<X>());
 }
 
-template <typename X>
-struct to_chars_test_base
-{
-    template <typename T, std::size_t N, typename... Ts>
-    TEST_CONSTEXPR_CXX23 void test(T v, char const (&expect)[N], Ts... args)
-    {
-        std::to_chars_result r;
-
-        constexpr std::size_t len = N - 1;
-        static_assert(len > 0, "expected output won't be empty");
-
-        if (!fits_in<X>(v))
-            return;
-
-        r = std::to_chars(buf, buf + len - 1, X(v), args...);
-        assert(r.ptr == buf + len - 1);
-        assert(r.ec == std::errc::value_too_large);
-
-        r = std::to_chars(buf, buf + sizeof(buf), X(v), args...);
-        assert(r.ptr == buf + len);
-        assert(r.ec == std::errc{});
-        assert(std::equal(buf, buf + len, expect));
-    }
-
-    template <typename... Ts>
-    TEST_CONSTEXPR_CXX23 void test_value(X v, Ts... args)
-    {
-        std::to_chars_result r;
-
-        // Poison the buffer for testing whether a successful std::to_chars
-        // doesn't modify data beyond r.ptr. Use unsigned values to avoid
-        // overflowing char when it's signed.
-        std::iota(buf, buf + sizeof(buf), static_cast<unsigned char>(1));
-        r = std::to_chars(buf, buf + sizeof(buf), v, args...);
-        assert(r.ec == std::errc{});
-        for (std::size_t i = r.ptr - buf; i < sizeof(buf); ++i)
-            assert(static_cast<unsigned char>(buf[i]) == i + 1);
-        *r.ptr = '\0';
-
-#ifndef TEST_HAS_NO_INT128
-        if (sizeof(X) == sizeof(__int128_t)) {
-            auto a = fromchars128_impl(buf, r.ptr, args...);
-            assert(v == a);
-        } else
-#endif
-        {
-            auto a = fromchars_impl(buf, r.ptr, args...);
-            assert(v == a);
-        }
-
-        auto ep = r.ptr - 1;
-        r = std::to_chars(buf, ep, v, args...);
-        assert(r.ptr == ep);
-        assert(r.ec == std::errc::value_too_large);
-    }
-
-private:
-    static TEST_CONSTEXPR_CXX23 long long fromchars_impl(char const* p, char const* ep, int base, true_type)
-    {
-        char* last;
-        long long r;
-        if (TEST_IS_CONSTANT_EVALUATED)
-          last = const_cast<char*>(std::from_chars(p, ep, r, base).ptr);
-        else
-          r = strtoll(p, &last, base);
-        assert(last == ep);
-
-        return r;
-    }
-
-    static TEST_CONSTEXPR_CXX23 unsigned long long fromchars_impl(char const* p, char const* ep, int base, false_type)
-    {
-        char* last;
-        unsigned long long r;
-        if (TEST_IS_CONSTANT_EVALUATED)
-          last = const_cast<char*>(std::from_chars(p, ep, r, base).ptr);
-        else
-          r = strtoull(p, &last, base);
-        assert(last == ep);
-
-        return r;
-    }
-#ifndef TEST_HAS_NO_INT128
-    static TEST_CONSTEXPR_CXX23 __int128_t fromchars128_impl(char const* p, char const* ep, int base, true_type)
-    {
-        if (!TEST_IS_CONSTANT_EVALUATED) {
-            char* last;
-            __int128_t r = strtoll(p, &last, base);
-            if(errno != ERANGE) {
-                assert(last == ep);
-                return r;
-            }
-        }
-
-        // When the value doesn't fit in a long long use from_chars. This is
-        // not ideal since it does a round-trip test instead if using an
-        // external source.
-        __int128_t r;
-        std::from_chars_result s = std::from_chars(p, ep, r, base);
-        assert(s.ec == std::errc{});
-        assert(s.ptr == ep);
-
-        return r;
-    }
-
-    static TEST_CONSTEXPR_CXX23 __uint128_t fromchars128_impl(char const* p, char const* ep, int base, false_type)
-    {
-        if (!TEST_IS_CONSTANT_EVALUATED) {
-            char* last;
-            __uint128_t r = strtoull(p, &last, base);
-            if(errno != ERANGE) {
-                assert(last == ep);
-                return r;
-            }
-        }
-
-        __uint128_t r;
-        std::from_chars_result s = std::from_chars(p, ep, r, base);
-        assert(s.ec == std::errc{});
-        assert(s.ptr == ep);
-
-        return r;
-    }
-
-    static TEST_CONSTEXPR_CXX23 auto fromchars128_impl(char const* p, char const* ep, int base = 10)
-    -> decltype(fromchars128_impl(p, ep, base, std::is_signed<X>()))
-    {
-        return fromchars128_impl(p, ep, base, std::is_signed<X>());
-    }
-
-#endif
-
-    static TEST_CONSTEXPR_CXX23 auto fromchars_impl(char const* p, char const* ep, int base = 10)
-    -> decltype(fromchars_impl(p, ep, base, std::is_signed<X>()))
-    {
-        return fromchars_impl(p, ep, base, std::is_signed<X>());
-    }
-
-    char buf[150];
-};
-
-template <typename X>
-struct roundtrip_test_base
-{
-    template <typename T, typename... Ts>
-    TEST_CONSTEXPR_CXX23 void test(T v, Ts... args)
-    {
-        std::from_chars_result r2;
-        std::to_chars_result r;
-        X x = 0xc;
-
-        if (fits_in<X>(v))
-        {
-            r = std::to_chars(buf, buf + sizeof(buf), v, args...);
-            assert(r.ec == std::errc{});
-
-            r2 = std::from_chars(buf, r.ptr, x, args...);
-            assert(r2.ptr == r.ptr);
-            assert(x == X(v));
-        }
-        else
-        {
-            r = std::to_chars(buf, buf + sizeof(buf), v, args...);
-            assert(r.ec == std::errc{});
-
-            r2 = std::from_chars(buf, r.ptr, x, args...);
-
-            TEST_DIAGNOSTIC_PUSH
-            TEST_MSVC_DIAGNOSTIC_IGNORED(4127) // conditional expression is constant
-
-            if (std::is_signed<T>::value && v < 0 && std::is_unsigned<X>::value)
-            {
-                assert(x == 0xc);
-                assert(r2.ptr == buf);
-                assert(r2.ec == std::errc::invalid_argument);
-            }
-            else
-            {
-                assert(x == 0xc);
-                assert(r2.ptr == r.ptr);
-                assert(r2.ec == std::errc::result_out_of_range);
-            }
-
-            TEST_DIAGNOSTIC_POP
-        }
-    }
-
-private:
-    char buf[150];
-};
-
-template <typename... T>
-struct type_list
-{
-};
-
-template <typename L1, typename L2>
-struct type_concat;
-
-template <typename... Xs, typename... Ys>
-struct type_concat<type_list<Xs...>, type_list<Ys...>>
-{
-    using type = type_list<Xs..., Ys...>;
-};
-
-template <typename L1, typename L2>
-using concat_t = typename type_concat<L1, L2>::type;
-
-template <typename L1, typename L2>
-constexpr auto concat(L1, L2) -> concat_t<L1, L2>
-{
-    return {};
-}
-
-auto all_signed = type_list<
-    char,
-    signed char,
-    short,
-    int,
-    long,
-    long long
-#ifndef TEST_HAS_NO_INT128
-    ,
-    __int128_t
-#endif
-    >();
-auto all_unsigned = type_list<
-    unsigned char,
-    unsigned short,
-    unsigned int,
-    unsigned long,
-    unsigned long long
-#ifndef TEST_HAS_NO_INT128
-    ,
-    __uint128_t
-#endif
-    >();
-auto integrals = concat(all_signed, all_unsigned);
-
-auto all_floats = type_list< float, double >(); //TODO: Add long double
-
-template <template <typename> class Fn, typename... Ts>
-TEST_CONSTEXPR_CXX23 void
-run(type_list<Ts...>)
-{
-    int ls[sizeof...(Ts)] = {(Fn<Ts>{}(), 0)...};
-    (void)ls;
-}
-
 #endif // SUPPORT_CHARCONV_TEST_HELPERS_H



More information about the libcxx-commits mailing list