4444#define YY_DOUBLE_SUPPORTED 0
4545#endif
4646
47- namespace Benchmarks {
48-
49- enum Algorithm {
50- DRAGON4 = 0 ,
51- ERROL3 = 1 ,
52- TO_STRING = 2 ,
53- FMT_FORMAT = 3 ,
54- NETLIB = 4 ,
55- SNPRINTF = 5 ,
56- GRISU2 = 6 ,
57- GRISU_EXACT = 7 ,
58- SCHUBFACH = 8 ,
59- DRAGONBOX = 9 ,
60- RYU = 10 ,
61- TEJU_JAGUA = 11 ,
62- DOUBLE_CONVERSION = 12 ,
63- ABSEIL = 13 ,
64- STD_TO_CHARS = 14 ,
65- GRISU3 = 15 ,
66- SWIFT_DTOA = 16 ,
67- YY_DOUBLE = 17 ,
68- COUNT // Keep last
69- };
70-
7147template <arithmetic_float T>
7248struct BenchArgs {
7349 using Type = T;
50+ using BenchFn = std::function<int (T, std::span<char >&, size_t fixed_size)>;
7451
75- BenchArgs (const std::string& name = {}, int (* func)(T, std::span< char >&) = {},
76- bool used = true , size_t testRepeat = 100 )
77- : name(name), func(func), used(used), testRepeat(testRepeat) {}
52+ BenchArgs (const std::string& name = {}, BenchFn func = {}, bool used = true ,
53+ size_t testRepeat = 100 , size_t fixedSize = 9 )
54+ : name(name), func(func), used(used), testRepeat(testRepeat), fixedSize(fixedSize) {}
7855
7956 std::string name{};
80- int (* func)(T, std::span< char >&) {};
57+ BenchFn func{};
8158 bool used{};
8259 size_t testRepeat{100 };
60+ size_t fixedSize{9 };
8361};
8462
63+ namespace BenchmarkShortest {
64+
8565template <arithmetic_float T>
8666int dragon4 (T d, std::span<char >& buffer) {
8767 if constexpr (std::is_same_v<T, float >)
@@ -187,14 +167,6 @@ int netlib(T d, std::span<char>& buffer) {
187167#endif
188168}
189169
190- template <arithmetic_float T>
191- int snprintf (T d, std::span<char >& buffer) {
192- if constexpr (std::is_same_v<T, float >)
193- return std::snprintf (buffer.data (), buffer.size (), " %.9g" , d);
194- else
195- return std::snprintf (buffer.data (), buffer.size (), " %.17g" , d);
196- }
197-
198170// grisu2 is hardcoded for double.
199171template <arithmetic_float T>
200172int grisu2 (T d, std::span<char >& buffer) {
@@ -284,19 +256,6 @@ int yy_double(T d, std::span<char>& buffer) {
284256#endif
285257}
286258
287- template <arithmetic_float T>
288- int abseil (T d, std::span<char >& buffer) {
289- // StrAppend is faster but only outputs 6 digits after the decimal point
290- // std::string s;
291- // absl::StrAppend(&s, d);
292- // std::copy(s.begin(), s.end(), buffer.begin());
293- // return size(s);
294- if constexpr (std::is_same_v<T, float >)
295- return absl::SNPrintF (buffer.data (), buffer.size (), " %.9g" , d);
296- else
297- return absl::SNPrintF (buffer.data (), buffer.size (), " %.17g" , d);
298- }
299-
300259template <arithmetic_float T>
301260int std_to_chars (T d, std::span<char >& buffer) {
302261#if TO_CHARS_SUPPORTED
@@ -313,34 +272,91 @@ int std_to_chars(T d, std::span<char>& buffer) {
313272#endif
314273}
315274
275+ } // namespace BenchmarksShortest
276+
277+ namespace BenchmarkFixedSize {
278+
279+ template <arithmetic_float T>
280+ int abseil (T d, std::span<char >& buffer, size_t fixed_size) {
281+ // StrAppend is faster but only outputs 6 digits after the decimal point
282+ // std::string s;
283+ // absl::StrAppend(&s, d);
284+ // std::copy(s.begin(), s.end(), buffer.begin());
285+ // return size(s);
286+ if constexpr (std::is_same_v<T, float >)
287+ return absl::SNPrintF (buffer.data (), buffer.size (), " %.9g" , d);
288+ else
289+ return absl::SNPrintF (buffer.data (), buffer.size (), " %.17g" , d);
290+ }
291+
292+ template <arithmetic_float T>
293+ int snprintf (T d, std::span<char >& buffer, size_t fixed_size) {
294+ if constexpr (std::is_same_v<T, float >)
295+ return std::snprintf (buffer.data (), buffer.size (), " %.9g" , d);
296+ else
297+ return std::snprintf (buffer.data (), buffer.size (), " %.17g" , d);
298+ }
299+
300+ } // namespace BenchmarksShortest
301+
302+ template <typename T>
303+ auto make_shortest_adapter (int (*fn)(T, std::span<char >&)) {
304+ return [fn](T v, std::span<char >& buf, size_t /* fixed_size*/ ) -> int {
305+ return fn (v, buf);
306+ };
307+ }
308+
309+ template <typename T>
310+ auto make_fixed_adapter (int (*fn)(T, std::span<char >&, size_t )) {
311+ return [fn](T v, std::span<char >& buf, size_t fixed_size) -> int {
312+ return fn (v, buf, fixed_size);
313+ };
314+ }
315+
316316template <arithmetic_float T>
317- std::array <BenchArgs<T>, Benchmarks::COUNT > initArgs (size_t fixed_size, bool use_errol = false ) {
318- if (fixed_size == 0 ) { // shortest length representation
319- std::array<BenchArgs<T>, Benchmarks::COUNT> args;
320- args[Benchmarks::DRAGON4] = { " dragon4 " , Benchmarks::dragon4 <T> , true , 10 } ;
321- args[Benchmarks::ERROL3] = { " errol3 " , Benchmarks::errol3<T> , ERROL_SUPPORTED && use_errol } ;
322- args[Benchmarks::TO_STRING] = { " std::to_string " , Benchmarks::to_string <T> , true } ;
323- args[Benchmarks::FMT_FORMAT] = { " fmt::format " , Benchmarks::fmt_format <T> , true } ;
324- args[Benchmarks::NETLIB] = { " netlib " , Benchmarks::netlib <T> , NETLIB_SUPPORTED && std::is_same_v<T, double >, 10 } ;
325- args[Benchmarks::SNPRINTF] = { " snprintf " , Benchmarks::snprintf <T> , true } ;
326- args[Benchmarks::GRISU2] = { " grisu2" , Benchmarks ::grisu2<T> , std::is_same_v<T, double > } ;
327- args[Benchmarks::GRISU_EXACT] = { " grisu_exact " , Benchmarks::grisu_exact <T> , true } ;
328- args[Benchmarks::SCHUBFACH] = { " schubfach " , Benchmarks::schubfach <T> , true } ;
329- args[Benchmarks::DRAGONBOX] = { " dragonbox " , Benchmarks::dragonbox <T> , true } ;
330- args[Benchmarks::RYU] = { " ryu " , Benchmarks::ryu <T> , true } ;
331- args[Benchmarks::TEJU_JAGUA] = { " teju_jagua " , Benchmarks::teju_jagua <T> , true } ;
332- args[Benchmarks::DOUBLE_CONVERSION] = { " double_conversion " , Benchmarks::double_conversion <T> , true } ;
333- args[Benchmarks::ABSEIL] = { " abseil " , Benchmarks::abseil <T> , ABSEIL_SUPPORTED } ;
334- args[Benchmarks::STD_TO_CHARS] = { " std::to_chars " , Benchmarks::std_to_chars <T> , TO_CHARS_SUPPORTED } ;
335- args[Benchmarks::GRISU3] = { " grisu3 " , Benchmarks::grisu3 <T> , std::is_same_v<T, double > } ;
336- args[Benchmarks::SWIFT_DTOA] = { " SwiftDtoa " , Benchmarks::swiftDtoa <T> , SWIFT_LIB_SUPPORTED } ;
337- args[Benchmarks::YY_DOUBLE] = { " yy_double " , Benchmarks::yy_double<T> , YY_DOUBLE_SUPPORTED && std::is_same_v<T, double > };
338- return args;
317+ std::vector <BenchArgs<T>> initArgs (bool use_errol = false , size_t repeat = 0 , size_t fixed_size = 0 ) {
318+ std::vector<BenchArgs<T>> args;
319+ if (fixed_size == 0 ) { // shortest-length representation
320+ auto && wrap = make_shortest_adapter <T>;
321+ namespace s = BenchmarkShortest ;
322+ args. emplace_back ( " dragon4 " , wrap (s::dragon4 <T>) , true , 10 ) ;
323+ args. emplace_back ( " netlib " , wrap (s::netlib <T>) , NETLIB_SUPPORTED && std::is_same_v<T, double > , 10 ) ;
324+ args. emplace_back ( " errol3 " , wrap (s::errol3 <T>) , ERROL_SUPPORTED && use_errol) ;
325+ args. emplace_back ( " fmt_format " , wrap (s::fmt_format <T>) , true ) ;
326+ args. emplace_back ( " grisu2" , wrap (s ::grisu2<T>) , std::is_same_v<T, double >) ;
327+ args. emplace_back ( " grisu3 " , wrap (s::grisu3 <T>) , std::is_same_v<T, double >) ;
328+ args. emplace_back ( " grisu_exact " , wrap (s::grisu_exact <T>) , true ) ;
329+ args. emplace_back ( " schubfach " , wrap (s::schubfach <T>) , true ) ;
330+ args. emplace_back ( " dragonbox " , wrap (s::dragonbox <T>) , true ) ;
331+ args. emplace_back ( " ryu " , wrap (s::ryu <T>) , true ) ;
332+ args. emplace_back ( " teju_jagua " , wrap (s::teju_jagua <T>) , true ) ;
333+ args. emplace_back ( " double_conversion " , wrap (s::double_conversion <T>) , true ) ;
334+ args. emplace_back ( " swiftDtoa " , wrap (s::swiftDtoa <T>) , SWIFT_LIB_SUPPORTED) ;
335+ args. emplace_back ( " yy_double " , wrap (s::yy_double <T>) , YY_DOUBLE_SUPPORTED && std::is_same_v<T, double >) ;
336+ args. emplace_back ( " std::to_chars " , wrap (s::std_to_chars <T>) , TO_CHARS_SUPPORTED) ;
337+
338+ // to_string, snprintf and abseil do not support shortest-length representation
339339 } else { // fixed-length representation
340- throw std::runtime_error (" fixed length representation not yet implemented" );
340+ auto && wrap = make_fixed_adapter<T>;
341+ namespace f = BenchmarkFixedSize;
342+ args.emplace_back (" snprintf" , wrap (f::snprintf<T>) , true );
343+ args.emplace_back (" abseil" , wrap (f::abseil<T>) , ABSEIL_SUPPORTED);
344+
345+ // to_string is hard-coded for 6 digits after the decimal point
346+ // args.emplace_back("to_string", BenchmarkFixedSize::to_string<T>, true);
347+
348+ fmt::println (" # testing fixed-size output to {} digits" , fixed_size);
349+ for (auto &arg : args)
350+ arg.fixedSize = fixed_size;
341351 }
342- };
343352
344- } // namespace Benchmarks
353+ if (repeat > 0 ) {
354+ fmt::println (" # forcing repeat count to {}" , repeat);
355+ for (auto &arg : args)
356+ arg.testRepeat = repeat;
357+ }
358+
359+ return args;
360+ };
345361
346362#endif
0 commit comments