1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
#include "fast_float/fast_float.h"
#include <cassert>
#include <cmath>
#include <cstdio>
#include <ios>
#include <iostream>
#include <limits>
#include <stdexcept>
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__)
// 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>
#include <sstream>
// 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
template <typename T> char *to_string(T d, char *buffer) {
auto written = std::snprintf(buffer, 64, "%.*e",
std::numeric_limits<T>::max_digits10 - 1, d);
return buffer + written;
}
void strtof_from_string(char const *st, float &d) {
char *pr = (char *)st;
#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 (pr == st) {
throw std::runtime_error("bug in strtod_from_string");
}
}
bool allvalues() {
char buffer[64];
for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) {
float v;
if ((w % 1048576) == 0) {
std::cout << ".";
std::cout.flush();
}
uint32_t word = uint32_t(w);
memcpy(&v, &word, sizeof(v));
if (std::isfinite(v)) {
float nextf = std::nextafterf(v, INFINITY);
if (copysign(1, v) != copysign(1, nextf)) {
continue;
}
if (!std::isfinite(nextf)) {
continue;
}
double v1{v};
assert(float(v1) == v);
double v2{nextf};
assert(float(v2) == nextf);
double midv{v1 + (v2 - v1) / 2};
float expected_midv = float(midv);
char const *string_end = to_string(midv, buffer);
float str_answer;
strtof_from_string(buffer, str_answer);
float result_value;
auto result = fast_float::from_chars(buffer, string_end, result_value);
// Starting with version 4.0 for fast_float, we return result_out_of_range
// if the value is either too small (too close to zero) or too large
// (effectively infinity). So std::errc::result_out_of_range is normal for
// well-formed input strings.
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cerr << "parsing error ? " << buffer << std::endl;
return false;
}
if (std::isnan(v)) {
if (!std::isnan(result_value)) {
std::cerr << "not nan" << buffer << std::endl;
std::cerr << "v " << std::hexfloat << v << std::endl;
std::cerr << "v2 " << std::hexfloat << v2 << std::endl;
std::cerr << "midv " << std::hexfloat << midv << std::endl;
std::cerr << "expected_midv " << std::hexfloat << expected_midv
<< std::endl;
return false;
}
} else if (copysign(1, result_value) != copysign(1, v)) {
std::cerr << buffer << std::endl;
std::cerr << "v " << std::hexfloat << v << std::endl;
std::cerr << "v2 " << std::hexfloat << v2 << std::endl;
std::cerr << "midv " << std::hexfloat << midv << std::endl;
std::cerr << "expected_midv " << std::hexfloat << expected_midv
<< std::endl;
std::cerr << "I got " << std::hexfloat << result_value
<< " but I was expecting " << v << std::endl;
return false;
} else if (result_value != str_answer) {
std::cerr << "no match ? " << buffer << std::endl;
std::cerr << "v " << std::hexfloat << v << std::endl;
std::cerr << "v2 " << std::hexfloat << v2 << std::endl;
std::cerr << "midv " << std::hexfloat << midv << std::endl;
std::cerr << "expected_midv " << std::hexfloat << expected_midv
<< std::endl;
std::cout << "started with " << std::hexfloat << midv << std::endl;
std::cout << "round down to " << std::hexfloat << str_answer
<< std::endl;
std::cout << "got back " << std::hexfloat << result_value << std::endl;
std::cout << std::dec;
return false;
}
}
}
std::cout << std::endl;
return true;
}
inline void Assert(bool Assertion) {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
defined(sun) || defined(__sun)
if (!Assertion) {
std::cerr << "Omitting hard failure on msys/cygwin/sun systems.";
}
#else
if (!Assertion) {
throw std::runtime_error("bug");
}
#endif
}
int main() {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
defined(sun) || defined(__sun)
std::cout << "Warning: msys/cygwin or solaris detected. This particular test "
"is likely to generate false failures due to our reliance on "
"the underlying runtime library as a gold standard."
<< std::endl;
#endif
Assert(allvalues());
std::cout << std::endl;
std::cout << "all ok" << std::endl;
return EXIT_SUCCESS;
}
|