Files
charconv/test/STL_benchmark.cpp
2023-08-04 14:37:05 -04:00

729 lines
27 KiB
C++

// (C) Copyright Stephan T. Lavavej 2019 - 2023.
// (C) Copyright Matt Borland 2023.
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Derived from mailing list post: https://lists.boost.org/Archives/boost/2023/05/254660.php
#ifndef _MSC_VER
#define AVOID_SPRINTF_S
#endif // _MSC_VER
#ifndef AVOID_CHARCONV
#include <charconv>
#endif // AVOID_CHARCONV
#include <array>
#include <chrono>
#include <exception>
#include <random>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <system_error>
#include <type_traits>
#include <vector>
#include <iostream>
#include <iomanip>
#include <limits>
#include <string>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/stl.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/charconv.hpp>
using namespace std;
using namespace std::chrono;
void verify(const bool b) {
if (!b) {
puts("VERIFICATION FAILURE");
exit(EXIT_FAILURE);
}
}
enum class RoundTrip { Sci, Fix, Gen, Hex, Lossy, u32, u64 };
constexpr size_t N = 2'000'000; // how many values to test
constexpr size_t K = 5; // how many times to repeat the test, for cleaner timing
constexpr size_t BufSize = 2'000; // more than enough
unsigned int global_dummy = 0;
template <typename Floating>
void test_boost_spirit_karma(const char* const str, const vector<Floating>& vec) {
namespace karma = boost::spirit::karma;
const auto start = steady_clock::now();
for (size_t k = 0; k < K; ++k) {
for (const auto& elem : vec) {
char buffer[BufSize];
char* it = buffer;
if constexpr (std::is_same_v<Floating, float>) {
karma::generate(it, karma::float_, elem);
} else {
karma::generate(it, karma::double_, elem);
}
}
}
const auto finish = steady_clock::now();
printf("%6.1f ns | %s\n", duration<double, nano>{finish - start}.count() / (N * K), str);
}
template <typename Floating>
void test_lexical_cast(const char* const str, const vector<Floating>& vec) {
const auto start = steady_clock::now();
for (size_t k = 0; k < K; ++k) {
for (const auto& elem : vec) {
const auto ret = boost::lexical_cast<array<char, 25>>(elem); // "-1.2345678901234567e-100" plus null term
global_dummy += static_cast<unsigned int>(ret[0]);
}
}
const auto finish = steady_clock::now();
printf("%6.1f ns | %s\n", duration<double, nano>{finish - start}.count() / (N * K), str);
}
template <typename Floating>
int sprintf_wrapper(char (&buf)[BufSize], const char* const fmt, const Floating elem) {
#ifdef AVOID_SPRINTF_S
return snprintf(buf, sizeof(buf), fmt, elem);
#else // AVOID_SPRINTF_S
return sprintf_s(buf, BufSize, fmt, elem);
#endif // AVOID_SPRINTF_S
}
template <RoundTrip RT, typename Floating>
void test_sprintf(const char* const str, const vector<Floating>& vec, const char* const fmt) {
char buf[BufSize];
const auto start = steady_clock::now();
for (size_t k = 0; k < K; ++k) {
for (const auto& elem : vec) {
const int ret = sprintf_wrapper(buf, fmt, elem);
global_dummy += static_cast<unsigned int>(ret);
global_dummy += static_cast<unsigned int>(buf[0]);
}
}
const auto finish = steady_clock::now();
printf("%6.1f ns | %s\n", duration<double, nano>{finish - start}.count() / (N * K), str);
for (const auto& elem : vec) {
verify(sprintf_wrapper(buf, fmt, elem) != -1);
if constexpr (RT == RoundTrip::Lossy) {
// skip lossy conversions
} else if constexpr (is_same_v<Floating, float>) {
verify(strtof(buf, nullptr) == elem);
} else {
verify(strtod(buf, nullptr) == elem);
}
}
}
#ifndef AVOID_CHARCONV
constexpr chars_format chars_format_from_RoundTrip(const RoundTrip rt) {
switch (rt) {
case RoundTrip::Sci:
return chars_format::scientific;
case RoundTrip::Fix:
return chars_format::fixed;
case RoundTrip::Gen:
return chars_format::general;
case RoundTrip::Hex:
return chars_format::hex;
case RoundTrip::Lossy:
default:
puts("CHARS FORMAT FAIL");
exit(EXIT_FAILURE);
}
}
constexpr boost::charconv::chars_format boost_chars_format_from_RoundTrip(const RoundTrip rt) {
switch (rt) {
case RoundTrip::Sci:
return boost::charconv::chars_format::scientific;
case RoundTrip::Fix:
return boost::charconv::chars_format::fixed;
case RoundTrip::Gen:
return boost::charconv::chars_format::general;
case RoundTrip::Hex:
return boost::charconv::chars_format::hex;
case RoundTrip::Lossy:
default:
puts("CHARS FORMAT FAIL");
exit(EXIT_FAILURE);
}
}
template <RoundTrip RT, typename Floating, typename... Args>
void test_STL_to_chars(const char* const str, const vector<Floating>& vec, const Args&... args) {
char buf[BufSize];
const auto start = steady_clock::now();
for (size_t k = 0; k < K; ++k) {
for (const auto& elem : vec) {
const auto result = to_chars(buf, buf + BufSize, elem, args...);
global_dummy += static_cast<unsigned int>(result.ptr - buf);
global_dummy += static_cast<unsigned int>(buf[0]);
}
}
const auto finish = steady_clock::now();
printf("%6.1f ns | %s\n", duration<double, nano>{finish - start}.count() / (N * K), str);
for (const auto& elem : vec) {
const auto result = to_chars(buf, buf + BufSize, elem, args...);
verify(result.ec == errc{});
if constexpr (RT == RoundTrip::Lossy) {
// skip lossy conversions
} else {
Floating round_trip;
const auto from_result = std::from_chars(buf, result.ptr, round_trip, chars_format_from_RoundTrip(RT));
verify(from_result.ec == errc{});
verify(from_result.ptr == result.ptr);
verify(round_trip == elem);
}
}
}
template <RoundTrip RT, typename Floating, typename... Args>
void test_boost_to_chars(const char* const str, const vector<Floating>& vec, const Args&... args) {
char buf[BufSize] {};
const auto start = steady_clock::now();
for (size_t k = 0; k < K; ++k) {
for (const auto& elem : vec) {
const auto result = to_chars(buf, buf + BufSize, elem, args...);
global_dummy += static_cast<unsigned int>(result.ptr - buf);
global_dummy += static_cast<unsigned int>(buf[0]);
}
}
const auto finish = steady_clock::now();
printf("%6.1f ns | %s\n", duration<double, nano>{finish - start}.count() / (N * K), str);
for (const auto& elem : vec) {
const auto result = to_chars(buf, buf + BufSize, elem, args...);
if (result.ec != errc())
{
std::cerr << "To chars failure with: " << elem
<< "\n to_chars value: " << buf << std::endl;
}
if constexpr (RT == RoundTrip::Lossy) {
// skip lossy conversions
} else {
Floating round_trip;
const auto from_result = boost::charconv::from_chars(buf, result.ptr, round_trip, boost_chars_format_from_RoundTrip(RT));
if (from_result.ec != errc() || from_result.ptr != result.ptr || round_trip != elem)
{
std::cerr << std::setprecision(std::numeric_limits<Floating>::digits10 + 1)
<< "Roundtrip failure with: " << elem
<< "\n to_chars val: " << buf
<< "\n from_chars val: " << round_trip
<< "\n from_chars ptr: " << static_cast<std::ptrdiff_t>(from_result.ptr - buf)
<< "\n to_chars ptr: " << static_cast<std::ptrdiff_t>(result.ptr - buf) << std::endl;
}
}
}
}
#endif // AVOID_CHARCONV
template <RoundTrip RT, typename T>
vector<char> prepare_strings(const vector<T>& vec) {
vector<char> output;
char buf[BufSize];
for (const auto& elem : vec) {
int ret;
if constexpr (RT == RoundTrip::Sci) {
if constexpr (is_same_v<T, float>) {
ret = sprintf_wrapper(buf, "%.8e", elem);
} else {
ret = sprintf_wrapper(buf, "%.16e", elem);
}
} else if constexpr (RT == RoundTrip::Hex) {
if constexpr (is_same_v<T, float>) {
ret = sprintf_wrapper(buf, "%.6a", elem);
} else {
ret = sprintf_wrapper(buf, "%.13a", elem);
}
} else if constexpr (RT == RoundTrip::u32) {
ret = sprintf_wrapper(buf, "%lu", elem);
} else {
static_assert(RT == RoundTrip::u64);
ret = sprintf_wrapper(buf, "%llu", elem);
}
verify(ret != -1);
output.insert(output.end(), buf, buf + ret + 1); // include null terminator
}
return output;
}
template <typename Floating>
void test_strtox(const char* const str, const vector<Floating>& original, const vector<char>& strings) {
vector<Floating> round_trip(N);
const auto start = steady_clock::now();
for (size_t k = 0; k < K; ++k) {
const char* ptr = strings.data();
char* endptr = nullptr;
for (size_t n = 0; n < N; ++n) {
if constexpr (is_same_v<Floating, float>) {
round_trip[n] = strtof(ptr, &endptr);
} else {
round_trip[n] = strtod(ptr, &endptr);
}
ptr = endptr + 1; // advance past null terminator
}
}
const auto finish = steady_clock::now();
printf("%6.1f ns | %s\n", duration<double, nano>{finish - start}.count() / (N * K), str);
verify(round_trip == original);
}
#ifndef AVOID_CHARCONV
vector<char> erase_0x(const vector<char>& strings) {
vector<char> output;
output.reserve(strings.size() - 2 * N);
for (auto i = strings.begin(); i != strings.end();) {
if (*i == '-') {
output.push_back('-');
i += 3; // advance past "-0x";
} else {
i += 2; // advance past "0x";
}
for (;;) {
const char c = *i++;
output.push_back(c);
if (c == '\0') {
break;
}
}
}
return output;
}
template <RoundTrip RT, typename Floating>
void test_from_chars(const char* const str, const vector<Floating>& original, const vector<char>& strings) {
const char* const last = strings.data() + strings.size();
vector<Floating> round_trip(N);
const auto start = steady_clock::now();
for (size_t k = 0; k < K; ++k) {
const char* first = strings.data();
for (size_t n = 0; n < N; ++n) {
const auto from_result = from_chars(first, last, round_trip[n], chars_format_from_RoundTrip(RT));
first = from_result.ptr + 1; // advance past null terminator
}
}
const auto finish = steady_clock::now();
printf("%6.1f ns | %s\n", duration<double, nano>{finish - start}.count() / (N * K), str);
verify(round_trip == original);
}
template <int base, typename Integer>
void test_from_chars_integer(const char* const str, const vector<Integer>& original, const vector<char>& strings) {
const char* const last = strings.data() + strings.size();
vector<Integer> round_trip(N);
const auto start = steady_clock::now();
for (size_t k = 0; k < K; ++k) {
const char* first = strings.data();
for (size_t n = 0; n < N; ++n) {
const auto from_result = from_chars(first, last, round_trip[n], base);
first = from_result.ptr + 1; // advance past null terminator
}
}
const auto finish = steady_clock::now();
printf("%6.1f ns | %s\n", duration<double, nano>{finish - start}.count() / (N * K), str);
verify(round_trip == original);
}
template <int base, typename Integer>
void test_boost_from_chars_integer(const char* const str, const vector<Integer>& original, const vector<char>& strings) {
const char* const last = strings.data() + strings.size();
vector<Integer> round_trip(N);
const auto start = steady_clock::now();
for (size_t k = 0; k < K; ++k) {
const char* first = strings.data();
for (size_t n = 0; n < N; ++n) {
const auto from_result = boost::charconv::from_chars(first, last, round_trip[n], base);
first = from_result.ptr + 1; // advance past null terminator
}
}
const auto finish = steady_clock::now();
printf("%6.1f ns | %s\n", duration<double, nano>{finish - start}.count() / (N * K), str);
verify(round_trip == original);
}
template <RoundTrip RT, typename Floating>
void test_boost_from_chars(const char* const str, const vector<Floating>& original, const vector<char>& strings) {
const char* const last = strings.data() + strings.size();
vector<Floating> round_trip(N);
const auto start = steady_clock::now();
for (size_t k = 0; k < K; ++k) {
const char* first = strings.data();
for (size_t n = 0; n < N; ++n) {
const auto from_result = boost::charconv::from_chars(first, last, round_trip[n], boost_chars_format_from_RoundTrip(RT));
first = from_result.ptr + 1; // advance past null terminator
}
}
const auto finish = steady_clock::now();
printf("%6.1f ns | %s\n", duration<double, nano>{finish - start}.count() / (N * K), str);
verify(round_trip == original);
}
template <typename Floating, typename Iterator>
bool parse_numbers(Iterator first, Iterator last, std::vector<Floating>& v)
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
using qi::double_;
using qi::float_;
using qi::phrase_parse;
using qi::_1;
using ascii::space;
using phoenix::push_back;
bool r = false;
if constexpr (std::is_same_v<Floating, float>)
{
r = phrase_parse(first, last,
// Begin grammar
(
float_[push_back(phoenix::ref(v), _1)]
>> *('\0' >> float_[push_back(phoenix::ref(v), _1)])
)
,
// End grammar
space);
}
else
{
static_assert(std::is_same_v<Floating, double>);
r = phrase_parse(first, last,
// Begin grammar
(
double_[push_back(phoenix::ref(v), _1)]
>> *('\0' >> double_[push_back(phoenix::ref(v), _1)])
)
,
// End grammar
space);
}
if (first != last) // fail if we did not get a full match
{
return false;
}
return r;
}
template <typename Floating>
void test_boost_spirit_qi(const char* const str, const vector<Floating>& /*original*/, const vector<char>& strings) {
const char* const last = strings.data() + strings.size();
vector<Floating> round_trip(N);
const auto start = steady_clock::now();
for (size_t k = 0; k < K; ++k) {
const char* first = strings.data();
string test_str(first, static_cast<std::ptrdiff_t>(last - first));
parse_numbers(test_str.begin(), test_str.end(), round_trip);
}
const auto finish = steady_clock::now();
printf("%6.1f ns | %s\n", duration<double, nano>{finish - start}.count() / (N * K), str);
// verify(round_trip == original);
}
/*
template <RoundTrip RT, typename Floating>
void test_boost_from_chars_parser(const char* const str, const vector<Floating>&, const vector<char>& strings) {
const char* const last = strings.data() + strings.size();
vector<Floating> round_trip(N);
bool sign {};
std::uint64_t significand {};
std::int64_t exponent {};
const auto start = steady_clock::now();
for (size_t k = 0; k < K; ++k) {
const char* first = strings.data();
for (size_t n = 0; n < N; ++n) {
const auto from_result = boost::charconv::detail::parser(first, last, sign, significand, exponent, boost_chars_format_from_RoundTrip(RT));
first = from_result.ptr + 1; // advance past null terminator
}
}
const auto finish = steady_clock::now();
printf("%6.1f ns | %s\n", duration<double, nano>{finish - start}.count() / (N * K), str);
// verify(round_trip == original);
}
*/
#endif // AVOID_CHARCONV
void test_all() {
#if defined(__clang__) && defined(_M_IX86)
const char* const toolset = "Clang/LLVM x86 + MSVC STL";
#elif defined(__clang__) && defined(_M_X64)
const char* const toolset = "Clang/LLVM x64 + MSVC STL";
#elif !defined(__clang__) && defined(_M_IX86)
const char* const toolset = "C1XX/C2 x86 + MSVC STL";
#elif !defined(__clang__) && defined(_M_X64)
const char* const toolset = "C1XX/C2 x64 + MSVC STL";
#elif defined(__clang__) && defined(__APPLE__) && defined(__arm64__)
const char* const toolset = "Clang/LLVM Apple ARM + libstdc++";
#elif !defined(__clang__) && defined(__APPLE__) && defined(__arm64__)
const char* const toolset = "GCC Apple ARM + libstdc++";
#elif defined(__clang__) && defined(__x86_64__)
const char* const toolset = "Clang/LLVM " BOOST_STRINGIZE(__clang_major__) " x64 + libstdc++"
#elif defined(__GNUC__) && defined(__x86_64__)
const char* const toolset = "GCC " BOOST_STRINGIZE(__GNUC__) " x64 + libstdc++" ;
#else
const char* const toolset = "Unknown Toolset";
#endif
puts(toolset);
vector<float> vec_flt;
vector<double> vec_dbl;
vector<uint32_t> vec_u32;
vector<uint64_t> vec_u64;
{
mt19937_64 mt64;
vec_flt.reserve(N);
while (vec_flt.size() < N) {
const uint32_t val = static_cast<uint32_t>(mt64());
constexpr uint32_t inf_nan = 0x7F800000U;
if ((val & inf_nan) == inf_nan) {
continue; // skip INF/NAN
}
float flt;
static_assert(sizeof(flt) == sizeof(val));
memcpy(&flt, &val, sizeof(flt));
vec_flt.push_back(flt);
}
vec_dbl.reserve(N);
while (vec_dbl.size() < N) {
const uint64_t val = mt64();
constexpr uint64_t inf_nan = 0x7FF0000000000000ULL;
if ((val & inf_nan) == inf_nan) {
continue; // skip INF/NAN
}
double dbl;
static_assert(sizeof(dbl) == sizeof(val));
memcpy(&dbl, &val, sizeof(dbl));
vec_dbl.push_back(dbl);
}
vec_u32.reserve(N);
while (vec_u32.size() < N) {
vec_u32.emplace_back(static_cast<uint32_t>(mt64()));
}
vec_u64.reserve(N);
while (vec_u64.size() < N) {
vec_u64.emplace_back(mt64());
}
}
test_lexical_cast("Boost.lexical_cast float", vec_flt);
test_lexical_cast("Boost.lexical_cast double", vec_dbl);
test_boost_spirit_karma("Boost.spirit.karma float", vec_flt);
test_boost_spirit_karma("Boost.spirit.karma double", vec_dbl);
test_sprintf<RoundTrip::Lossy>("std::sprintf float plain shortest", vec_flt, "%.g");
test_sprintf<RoundTrip::Lossy>("std::sprintf double plain shortest", vec_dbl, "%.g");
/*
test_sprintf<RoundTrip::Sci>("std::sprintf float scientific 8", vec_flt, "%.8e");
test_sprintf<RoundTrip::Sci>("std::sprintf double scientific 16", vec_dbl, "%.16e");
test_sprintf<RoundTrip::Lossy>("std::sprintf float fixed 6 (lossy)", vec_flt, "%f");
test_sprintf<RoundTrip::Lossy>("std::sprintf double fixed 6 (lossy)", vec_dbl, "%f");
test_sprintf<RoundTrip::Gen>("std::sprintf float general 9", vec_flt, "%.9g");
test_sprintf<RoundTrip::Gen>("std::sprintf double general 17", vec_dbl, "%.17g");
test_sprintf<RoundTrip::Hex>("std::sprintf float hex 6", vec_flt, "%.6a");
test_sprintf<RoundTrip::Hex>("std::sprintf double hex 13", vec_dbl, "%.13a");
*/
#ifndef AVOID_CHARCONV
test_STL_to_chars<RoundTrip::Gen>("std::to_chars float plain shortest", vec_flt);
test_STL_to_chars<RoundTrip::Gen>("std::to_chars double plain shortest", vec_dbl);
test_boost_to_chars<RoundTrip::Gen>("Boost.Charconv::to_chars float plain shortest", vec_flt);
test_boost_to_chars<RoundTrip::Gen>("Boost.Charconv::to_chars double plain shortest", vec_dbl);
/*
test_STL_to_chars<RoundTrip::Sci>("std::to_chars float scientific shortest", vec_flt, chars_format::scientific);
test_STL_to_chars<RoundTrip::Sci>("std::to_chars double scientific shortest", vec_dbl, chars_format::scientific);
test_boost_to_chars<RoundTrip::Gen>("Boost.Charconv::to_chars float scientific shortest", vec_flt, boost::charconv::chars_format::scientific);
test_boost_to_chars<RoundTrip::Gen>("Boost.Charconv::to_chars double scientific shortest", vec_dbl, boost::charconv::chars_format::scientific);
test_STL_to_chars<RoundTrip::Fix>("std::to_chars float fixed shortest", vec_flt, chars_format::fixed);
test_STL_to_chars<RoundTrip::Fix>("std::to_chars double fixed shortest", vec_dbl, chars_format::fixed);
test_boost_to_chars<RoundTrip::Gen>("Boost.Charconv::to_chars float fixed shortest", vec_flt, boost::charconv::chars_format::fixed);
test_boost_to_chars<RoundTrip::Gen>("Boost.Charconv::to_chars double fixed shortest", vec_dbl, boost::charconv::chars_format::fixed);
test_STL_to_chars<RoundTrip::Gen>("std::to_chars float general shortest", vec_flt, chars_format::general);
test_STL_to_chars<RoundTrip::Gen>("std::to_chars double general shortest", vec_dbl, chars_format::general);
test_boost_to_chars<RoundTrip::Gen>("Boost.Charconv::to_chars float general shortest", vec_flt, boost::charconv::chars_format::general);
test_boost_to_chars<RoundTrip::Gen>("Boost.Charconv::to_chars double general shortest", vec_dbl, boost::charconv::chars_format::general);
test_STL_to_chars<RoundTrip::Hex>("std::to_chars float hex shortest", vec_flt, chars_format::hex);
test_STL_to_chars<RoundTrip::Hex>("std::to_chars double hex shortest", vec_dbl, chars_format::hex);
test_boost_to_chars<RoundTrip::Hex>("Boost.Charconv::to_chars float hex shortest", vec_flt, boost::charconv::chars_format::hex);
test_boost_to_chars<RoundTrip::Hex>("Boost.Charconv::to_chars double hex shortest", vec_dbl, boost::charconv::chars_format::hex);
test_STL_to_chars<RoundTrip::Sci>("std::to_chars float scientific 8", vec_flt, chars_format::scientific, 8);
test_STL_to_chars<RoundTrip::Sci>("std::to_chars double scientific 16", vec_dbl, chars_format::scientific, 16);
test_boost_to_chars<RoundTrip::Sci>("Boost.Charconv::to_chars float scientific 8", vec_flt, boost::charconv::chars_format::scientific, 8);
test_boost_to_chars<RoundTrip::Sci>("Boost.Charconv::to_chars double scientific 16", vec_dbl, boost::charconv::chars_format::scientific, 16);
test_STL_to_chars<RoundTrip::Lossy>("std::to_chars float fixed 6 (lossy)", vec_flt, chars_format::fixed, 6);
test_STL_to_chars<RoundTrip::Lossy>("std::to_chars double fixed 6 (lossy)", vec_dbl, chars_format::fixed, 6);
test_boost_to_chars<RoundTrip::Lossy>("Boost.Charconv::to_chars float fixed 6 (lossy)", vec_flt, boost::charconv::chars_format::fixed, 6);
test_boost_to_chars<RoundTrip::Lossy>("Boost.Charconv::to_chars double fixed 6 (lossy)", vec_dbl, boost::charconv::chars_format::fixed, 6);
test_STL_to_chars<RoundTrip::Gen>("std::to_chars float general 9", vec_flt, chars_format::general, 9);
test_STL_to_chars<RoundTrip::Gen>("std::to_chars double general 17", vec_dbl, chars_format::general, 17);
test_boost_to_chars<RoundTrip::Gen>("Boost.Charconv::to_chars float general 9", vec_flt, chars_format::general, 9);
test_boost_to_chars<RoundTrip::Gen>("Boost.Charconv::to_chars double general 17", vec_dbl, chars_format::general, 17);
test_STL_to_chars<RoundTrip::Hex>("std::to_chars float hex 6", vec_flt, chars_format::hex, 6);
test_STL_to_chars<RoundTrip::Hex>("std::to_chars double hex 13", vec_dbl, chars_format::hex, 13);
test_boost_to_chars<RoundTrip::Hex>("Boost.Charconv::to_chars float hex 6", vec_flt, chars_format::hex, 6);
test_boost_to_chars<RoundTrip::Hex>("Boost.Charconv::to_chars double hex 13", vec_dbl, chars_format::hex, 13);
*/
#endif // AVOID_CHARCONV
puts("----------");
const vector<char> strings_sci_flt = prepare_strings<RoundTrip::Sci>(vec_flt);
const vector<char> strings_sci_dbl = prepare_strings<RoundTrip::Sci>(vec_dbl);
const vector<char> strings_hex_flt = prepare_strings<RoundTrip::Hex>(vec_flt);
const vector<char> strings_hex_dbl = prepare_strings<RoundTrip::Hex>(vec_dbl);
const vector<char> strings_u64 = prepare_strings<RoundTrip::u64>(vec_u64);
test_strtox("std::strtof float scientific", vec_flt, strings_sci_flt);
test_strtox("std::strtod double scientific", vec_dbl, strings_sci_dbl);
//test_strtox("std::strtof float hex", vec_flt, strings_hex_flt);
//test_strtox("std::strtod double hex", vec_dbl, strings_hex_dbl);
test_from_chars<RoundTrip::Sci>("std::from_chars float scientific", vec_flt, strings_sci_flt);
test_from_chars<RoundTrip::Sci>("std::from_chars double scientific", vec_dbl, strings_sci_dbl);
test_boost_from_chars<RoundTrip::Sci>("Boost.Charconv::from_chars float scientific", vec_flt, strings_sci_flt);
test_boost_from_chars<RoundTrip::Sci>("Boost.Charconv::from_chars double scientific", vec_dbl, strings_sci_dbl);
test_boost_spirit_qi<float>("Boost.Spirit.Qi float scientific", vec_flt, strings_sci_flt);
test_boost_spirit_qi<double>("Boost.Spirit.Qi double scientific", vec_dbl, strings_sci_dbl);
//test_boost_from_chars_parser<RoundTrip::Sci>("Boost.Charconv::from_chars::parser float scientific", vec_flt, strings_sci_flt);
//test_boost_from_chars_parser<RoundTrip::Sci>("Boost.Charconv::from_chars::parser double scientific", vec_dbl, strings_sci_dbl);
test_from_chars_integer<10>("std::from_chars uint64_t", vec_u64, strings_u64);
test_boost_from_chars_integer<10>("Boost.Charconv::from_chars uint64_t", vec_u64, strings_u64);
//test_from_chars<RoundTrip::Hex>("std::from_chars float hex", vec_flt, erase_0x(strings_hex_flt));
//test_from_chars<RoundTrip::Hex>("std::from_chars double hex", vec_dbl, erase_0x(strings_hex_dbl));
//test_boost_from_chars<RoundTrip::Hex>("Boost.Charconv::from_chars float hex", vec_flt, erase_0x(strings_hex_flt));
//test_boost_from_chars<RoundTrip::Hex>("Boost.Charconv::from_chars double hex", vec_dbl, erase_0x(strings_hex_dbl));
printf("global_dummy: %u\n", global_dummy);
}
#ifdef BOOST_CHARCONV_RUN_BENCHMARKS
int main()
{
try {
test_all();
} catch (const exception& e) {
printf("Exception: %s\n", e.what());
} catch (...) {
printf("Unknown exception.\n");
}
return 1;
}
#else
int main()
{
return 0;
}
#endif