| #include "fast_float/fast_float.h" |
| |
| #include <ios> |
| #include <iostream> |
| #include <random> |
| #include <sstream> |
| #include <string> |
| #include <system_error> |
| #include <utility> |
| #include <vector> |
| |
| #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ |
| defined(sun) || defined(__sun) |
| // Anything at all that is related to cygwin, msys and so forth will |
| // always use this fallback because we cannot rely on it behaving as normal |
| // gcc. |
| #include <locale> |
| |
| // workaround for CYGWIN |
| double cygwin_strtod_l(char const *start, char **end) { |
| double d; |
| std::stringstream ss; |
| ss.imbue(std::locale::classic()); |
| ss << start; |
| ss >> d; |
| if (ss.fail()) { |
| *end = nullptr; |
| } |
| if (ss.eof()) { |
| ss.clear(); |
| } |
| auto nread = ss.tellg(); |
| *end = const_cast<char *>(start) + nread; |
| return d; |
| } |
| |
| float cygwin_strtof_l(char const *start, char **end) { |
| float d; |
| std::stringstream ss; |
| ss.imbue(std::locale::classic()); |
| ss << start; |
| ss >> d; |
| if (ss.fail()) { |
| *end = nullptr; |
| } |
| if (ss.eof()) { |
| ss.clear(); |
| } |
| auto nread = ss.tellg(); |
| *end = const_cast<char *>(start) + nread; |
| return d; |
| } |
| #endif |
| |
| std::pair<double, bool> strtod_from_string(char const *st) { |
| double d; |
| char *pr; |
| #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ |
| defined(sun) || defined(__sun) |
| d = cygwin_strtod_l(st, &pr); |
| #elif defined(_WIN32) |
| static _locale_t c_locale = _create_locale(LC_ALL, "C"); |
| d = _strtod_l(st, &pr, c_locale); |
| #else |
| static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); |
| d = strtod_l(st, &pr, c_locale); |
| #endif |
| if (st == pr) { |
| std::cerr << "strtod_l could not parse '" << st << std::endl; |
| return std::make_pair(0, false); |
| } |
| return std::make_pair(d, true); |
| } |
| |
| std::pair<float, bool> strtof_from_string(char *st) { |
| float d; |
| char *pr; |
| #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ |
| defined(sun) || defined(__sun) |
| d = cygwin_strtof_l(st, &pr); |
| #elif defined(_WIN32) |
| static _locale_t c_locale = _create_locale(LC_ALL, "C"); |
| d = _strtof_l(st, &pr, c_locale); |
| #else |
| static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); |
| d = strtof_l(st, &pr, c_locale); |
| #endif |
| if (st == pr) { |
| std::cerr << "strtof_l could not parse '" << st << std::endl; |
| return std::make_pair(0.0f, false); |
| } |
| return std::make_pair(d, true); |
| } |
| |
| bool tester() { |
| std::random_device rd; |
| std::mt19937 gen(rd()); |
| for (int q = 18; q <= 27; q++) { |
| std::cout << "q = " << -q << std::endl; |
| uint64_t power5 = 1; |
| for (int k = 0; k < q; k++) { |
| power5 *= 5; |
| } |
| uint64_t low_threshold = 0x20000000000000 / power5 + 1; |
| uint64_t threshold = 0xFFFFFFFFFFFFFFFF / power5; |
| std::uniform_int_distribution<uint64_t> dis(low_threshold, threshold); |
| for (size_t i = 0; i < 10000; i++) { |
| uint64_t mantissa = dis(gen) * power5; |
| std::stringstream ss; |
| ss << mantissa; |
| ss << "e"; |
| ss << -q; |
| std::string to_be_parsed = ss.str(); |
| std::pair<double, bool> expected_double = |
| strtod_from_string(to_be_parsed.c_str()); |
| double result_value; |
| auto result = fast_float::from_chars( |
| to_be_parsed.data(), to_be_parsed.data() + to_be_parsed.size(), |
| result_value); |
| if (result.ec != std::errc() && |
| result.ec != std::errc::result_out_of_range) { |
| std::cout << to_be_parsed << std::endl; |
| std::cerr << " I could not parse " << std::endl; |
| return false; |
| } |
| if (result_value != expected_double.first) { |
| std::cout << to_be_parsed << std::endl; |
| std::cerr << std::hexfloat << result_value << std::endl; |
| std::cerr << std::hexfloat << expected_double.first << std::endl; |
| std::cerr << " Mismatch " << std::endl; |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| int main() { |
| if (tester()) { |
| std::cout << std::endl; |
| std::cout << "all ok" << std::endl; |
| return EXIT_SUCCESS; |
| } |
| std::cerr << std::endl; |
| std::cerr << "errors were encountered" << std::endl; |
| return EXIT_FAILURE; |
| } |