1919#include " double-conversion/double-conversion.h"
2020#include " grisu_exact.h"
2121#include " dragon4.h"
22+ #include " schubfach_32.h"
2223#include " schubfach_64.h"
24+
2325#if __has_include("errol.h")
2426#include " errol.h"
25- #define ERROL_SUPPORTED 1
26- #else
27- #define ERROL_SUPPORTED 0
27+ #define ERROL_SUPPORTED
2828#endif
2929
3030#define IEEE_8087
3939#include " random_generators.h"
4040#include " ieeeToString.h"
4141
42- #include < charconv>
42+ #include < fmt/format.h>
43+
4344#include < climits>
4445#include < cmath>
4546#include < cstdio>
46- #include < cstdlib>
47- #include < cstring>
48- #include < float.h>
49- #include < fmt/format.h>
5047#include < fstream>
5148#include < iostream>
52- #include < limits.h>
53- #include < stdio.h>
5449#include < string>
5550#include < vector>
5651
57- #if FROM_CHARS_DOUBLE_SUPPORTED
52+ #if FROM_CHARS_SUPPORTED
5853#include < charconv>
5954#endif
6055
61- void process (std::vector<double > &lines) {
62- pretty_print (lines, " dragon4" , [](const std::vector<double > &lines) {
63- double volume = 0 ;
56+ template <typename T>
57+ concept arithmetic_float
58+ = std::is_same_v<T, float > || std::is_same_v<T, double >;
59+
60+ template <arithmetic_float T>
61+ void process (const std::vector<T> &lines) {
62+ using MantissaType = std::conditional_t <std::is_same_v<T, float >,
63+ uint32_t , uint64_t >;
64+ using IEEE754Type = std::conditional_t <std::is_same_v<T, float >,
65+ IEEE754f, IEEE754d>;
66+
67+ // No dragon4 implementation optimized for float instead of double ?
68+ pretty_print (lines, " dragon4" , [](const std::vector<T> &lines) -> int {
69+ int volume = 0 ;
6470 for (const auto d : lines) {
6571 uint64_t dmantissa;
6672 int dexp;
67- const IEEE754d fields = decode_ieee754 (d);
73+ const IEEE754Type fields = decode_ieee754 (d);
6874 dragon4::Dragon4 (dmantissa, dexp, fields.mantissa , fields.exponent ,
6975 true , true );
7076 char buffer[100 ];
7177 volume += to_chars (dmantissa, dexp, fields.sign , buffer);
7278 }
7379 return volume;
74- });
75-
76- #if ERROL_SUPPORTED
77- pretty_print (lines, " errol3" , [](const std::vector<double > &lines) {
78- double volume = 0 ;
80+ }, 10 );
81+
82+ #ifdef ERROL_SUPPORTED
83+ // No errol3 implementation optimized for float instead of double ?
84+ pretty_print (lines, " errol3" , [](const std::vector<T> &lines) -> int {
85+ int volume = 0 ;
7986 char buffer[100 ];
8087 for (const auto d : lines) {
81- errol3_dtoa (d, buffer); // returns the exponent?
88+ errol3_dtoa (d, buffer); // returns the exponent
8289 volume += std::strlen (buffer);
8390 }
8491 return volume;
8592 });
8693#else
8794 std::cout << " # errol not supported" << std::endl;
88- #endif // ERROL_SUPPORTED
89- pretty_print (lines, " std::to_string" , [](const std::vector<double > &lines) {
90- double volume = 0 ;
95+ #endif
96+
97+ pretty_print (lines, " std::to_string" , [](const std::vector<T> &lines) -> int {
98+ int volume = 0 ;
9199 for (const auto d : lines) {
92100 const std::string s = std::to_string (d);
93101 volume += s.size ();
94102 }
95103 return volume;
96104 });
97105
98- pretty_print (lines, " fmt::format" , [](const std::vector<double > &lines) {
99- double volume = 0 ;
106+ pretty_print (lines, " fmt::format" , [](const std::vector<T > &lines) -> int {
107+ int volume = 0 ;
100108 for (const auto d : lines) {
101109 const std::string s = fmt::format (" {}" , d);
102110 volume += s.size ();
@@ -105,8 +113,9 @@ void process(std::vector<double> &lines) {
105113 });
106114
107115#if NETLIB_SUPPORTED
108- pretty_print (lines, " netlib" , [](const std::vector<double > &lines) {
109- double volume = 0 ;
116+ // There's no "ftoa", only "dtoa", so not optimized for float.
117+ pretty_print (lines, " netlib" , [](const std::vector<T> &lines) -> int {
118+ int volume = 0 ;
110119 char *result;
111120 int decpt, sign;
112121 char *rve;
@@ -126,17 +135,19 @@ void process(std::vector<double> &lines) {
126135 std::cout << " # netlib not supported" << std::endl;
127136#endif
128137
129- pretty_print (lines, " sprintf" , [](const std::vector<double > &lines) {
130- double volume = 0 ;
138+ pretty_print (lines, " sprintf" , [](const std::vector<T > &lines) -> int {
139+ int volume = 0 ;
131140 char buffer[100 ];
132141 for (const auto d : lines) {
133142 volume += snprintf (buffer, sizeof (buffer), " %g" , d);
134143 }
135144 return volume;
136145 });
137146
138- pretty_print (lines, " grisu2" , [](const std::vector<double > &lines) {
139- double volume = 0 ;
147+ // grisu2::dtoa_impl::grisu2 can take a template type
148+ // However, grisu2::to_chars is hardcoded for double.
149+ pretty_print (lines, " grisu2" , [](const std::vector<T> &lines) -> int {
150+ int volume = 0 ;
140151 char buffer[100 ];
141152 for (const auto d : lines) {
142153 const char *newp = grisu2::to_chars (buffer, nullptr , d);
@@ -145,8 +156,8 @@ void process(std::vector<double> &lines) {
145156 return volume;
146157 });
147158
148- pretty_print (lines, " grisu_exact" , [](const std::vector<double > &lines) {
149- double volume = 0 ;
159+ pretty_print (lines, " grisu_exact" , [](const std::vector<T > &lines) -> int {
160+ int volume = 0 ;
150161 char buffer[100 ];
151162 for (const auto d : lines) {
152163 auto v = jkj::grisu_exact (d);
@@ -155,18 +166,20 @@ void process(std::vector<double> &lines) {
155166 return volume;
156167 });
157168
158- pretty_print (lines, " schubfach" , [](const std::vector<double > &lines) {
159- double volume = 0 ;
169+ pretty_print (lines, " schubfach" , [](const std::vector<T > &lines) -> int {
170+ int volume = 0 ;
160171 char buffer[100 ];
161172 for (const auto d : lines) {
162- const char *end_ptr = schubfach::Dtoa (buffer, d);
173+ const char * end_ptr = std::is_same_v<T, float >
174+ ? schubfach::Ftoa (buffer, d)
175+ : schubfach::Dtoa (buffer, d);
163176 volume += end_ptr - &buffer[0 ];
164177 }
165178 return volume;
166179 });
167180
168- pretty_print (lines, " dragonbox" , [](const std::vector<double > &lines) {
169- double volume = 0 ;
181+ pretty_print (lines, " dragonbox" , [](const std::vector<T > &lines) -> int {
182+ int volume = 0 ;
170183 char buffer[100 ];
171184 for (const auto d : lines) {
172185 const char *end_ptr = jkj::dragonbox::to_chars (d, buffer);
@@ -175,38 +188,42 @@ void process(std::vector<double> &lines) {
175188 return volume;
176189 });
177190
178- pretty_print (lines, " ryu" , [](const std::vector<double > &lines) {
179- double volume = 0 ;
191+ pretty_print (lines, " ryu" , [](const std::vector<T > &lines) -> int {
192+ int volume = 0 ;
180193 char buffer[100 ];
181194 for (const auto d : lines) {
182- volume += d2s_buffered_n (d, buffer);
195+ volume += std::is_same_v<T, float > ? f2s_buffered_n (d, buffer)
196+ : d2s_buffered_n (d, buffer);
183197 }
184198 return volume;
185199 });
186200
187- pretty_print (lines, " teju_jagua" , [](const std::vector<double > &lines) {
188- double volume = 0 ;
201+ pretty_print (lines, " teju_jagua" , [](const std::vector<T > &lines) -> int {
202+ int volume = 0 ;
189203 char buffer[100 ];
190204 for (const auto d : lines) {
191- const auto fields = teju::traits_t <double >::teju (d);
192- const bool sign = (* reinterpret_cast < const uint64_t *>(&d) >> 63 ) & 1 ;
205+ const auto fields = teju::traits_t <T >::teju (d);
206+ const bool sign = std::signbit (d) ;
193207 volume += to_chars (fields.mantissa , fields.exponent , sign, buffer);
194208 }
195209 return volume;
196210 });
197211
198- pretty_print (lines, " double_conversion" , [](const std::vector<double > &lines) {
199- double volume = 0 ;
212+ pretty_print (lines, " double_conversion" , [](const std::vector<T> &lines) -> int {
213+ int volume = 0 ;
214+ constexpr int kBufferSize = 100 ;
215+ char buffer[kBufferSize ];
200216 const double_conversion::DoubleToStringConverter converter (
201217 double_conversion::DoubleToStringConverter::NO_FLAGS, " inf" , " nan" , ' e' ,
202218 -4 , 6 , 0 , 0 );
203- const int kBufferSize = 100 ;
204- char buffer[kBufferSize ];
205219 double_conversion::StringBuilder builder (buffer, kBufferSize );
206220
207221 for (const auto d : lines) {
208222 builder.Reset ();
209- if (!converter.ToShortest (d, &builder)) {
223+ const bool valid = std::is_same_v<T, float >
224+ ? converter.ToShortestSingle (d, &builder)
225+ : converter.ToShortest (d, &builder);
226+ if (!valid) {
210227 std::cerr << " problem with " << d << std::endl;
211228 std::abort ();
212229 }
@@ -215,8 +232,8 @@ void process(std::vector<double> &lines) {
215232 return volume;
216233 });
217234
218- pretty_print (lines, " abseil" , [](const std::vector<double > &lines) {
219- double volume = 0 ;
235+ pretty_print (lines, " abseil" , [](const std::vector<T > &lines) -> int {
236+ int volume = 0 ;
220237 std::string buffer;
221238 for (const auto d : lines) {
222239 buffer.clear ();
@@ -226,10 +243,9 @@ void process(std::vector<double> &lines) {
226243 return volume;
227244 });
228245
229-
230- #if FROM_CHARS_DOUBLE_SUPPORTED
231- pretty_print (lines, " std::to_chars" , [](const std::vector<double > &lines) {
232- double volume = 0 ;
246+ #if FROM_CHARS_SUPPORTED
247+ pretty_print (lines, " std::to_chars" , [](const std::vector<T> &lines) -> int {
248+ int volume = 0 ;
233249 char buffer[100 ];
234250 for (const auto d : lines) {
235251 const auto [p, ec] = std::to_chars (buffer, buffer + sizeof (buffer), d);
@@ -244,24 +260,25 @@ void process(std::vector<double> &lines) {
244260#else
245261 std::cout << " # std::to_chars not supported" << std::endl;
246262#endif
247-
248263}
249264
250- void fileload (const char *filename) {
265+ template <typename T>
266+ void fileload (const std::string &filename) {
251267 std::ifstream inputfile (filename);
252268 if (!inputfile) {
253269 std::cerr << " can't open " << filename << std::endl;
254270 return ;
255271 }
256272
257- std::vector<double > lines;
273+ std::vector<T > lines;
258274 lines.reserve (10000 ); // let us reserve plenty of memory.
259275 for (std::string line; getline (inputfile, line);) {
260276 try {
261- lines.push_back (std::stod (line));
277+ lines.push_back (std::is_same_v<T, float > ? std::stof (line)
278+ : std::stod (line));
262279 } catch (...) {
263- std::cerr << " problem with " << line << std::endl;
264- std::cerr << " We expect floating-point numbers (one per line)."
280+ std::cerr << " problem with " << line << " \n "
281+ << " We expect floating-point numbers (one per line)."
265282 << std::endl;
266283 std::abort ();
267284 }
@@ -270,15 +287,16 @@ void fileload(const char *filename) {
270287 process (lines);
271288}
272289
273- void parse_random_numbers (size_t howmany, std::string random_model) {
290+ template <typename T>
291+ void parse_random_numbers (size_t howmany, const std::string &random_model) {
274292 std::cout << " # parsing random numbers" << std::endl;
275- std::vector<double > lines;
276- auto g = get_generator_by_name (random_model);
277- std::cout << " model: " << g->describe () << std::endl;
278- std::cout << " volume: " << howmany << " floats" << std::endl;
293+ std::vector<T > lines;
294+ auto g = get_generator_by_name<T> (random_model);
295+ std::cout << " model: " << g->describe () << " \n "
296+ << " volume: " << howmany << " floats" << std::endl;
279297 lines.reserve (howmany); // let us reserve plenty of memory.
280298 for (size_t i = 0 ; i < howmany; i++) {
281- double line = g->new_float ();
299+ const T line = g->new_float ();
282300 lines.push_back (line);
283301 }
284302 process (lines);
@@ -296,22 +314,37 @@ int main(int argc, char **argv) {
296314 cxxopts::value<size_t >()->default_value (" 100000" ))(
297315 " m,model" , " Random Model." ,
298316 cxxopts::value<std::string>()->default_value (" uniform" ))(
317+ " s,single" , " Use single precision instead of double." ,
318+ cxxopts::value<bool >()->default_value (" false" ))(
299319 " h,help" , " Print usage." );
300- auto result = options.parse (argc, argv);
320+ const auto result = options.parse (argc, argv);
321+
301322 if (result[" help" ].as <bool >()) {
302323 std::cout << options.help () << std::endl;
303324 return EXIT_SUCCESS;
304325 }
305- auto filename = result[" file" ].as <std::string>();
326+
327+ const bool single = result[" single" ].as <bool >();
328+ std::cout << " number type: " << (single ? " binary32 (float)" : " binary64 (double)" ) << std::endl;
329+
330+ const auto filename = result[" file" ].as <std::string>();
306331 if (filename.empty ()) {
307- parse_random_numbers (result[" volume" ].as <size_t >(),
308- result[" model" ].as <std::string>());
309- std::cout << " # You can also provide a filename (with the -f flag): it "
310- " should contain one "
311- " string per line corresponding to a number"
332+ if (single) {
333+ parse_random_numbers<float >(result[" volume" ].as <size_t >(),
334+ result[" model" ].as <std::string>());
335+ } else {
336+ parse_random_numbers<double >(result[" volume" ].as <size_t >(),
337+ result[" model" ].as <std::string>());
338+ }
339+ std::cout << " # You can also provide a filename (with the -f flag):"
340+ " it should contain one string per line corresponding to a number"
312341 << std::endl;
313- } else {
314- fileload (filename.c_str ());
342+ }
343+ else {
344+ if (single)
345+ fileload<float >(filename);
346+ else
347+ fileload<double >(filename);
315348 }
316349 } catch (const std::exception &e) {
317350 std::cout << " error parsing options: " << e.what () << std::endl;
0 commit comments