|
1 | 1 | #include "fast_float/fast_float.h" |
2 | 2 |
|
| 3 | +#include <atomic> |
3 | 4 | #include <cassert> |
4 | 5 | #include <cmath> |
5 | 6 | #include <cstdio> |
6 | 7 | #include <ios> |
7 | 8 | #include <iostream> |
8 | 9 | #include <limits> |
9 | 10 | #include <stdexcept> |
| 11 | +#include <thread> |
| 12 | +#include <vector> |
10 | 13 |
|
11 | 14 | #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) |
12 | 15 | // Anything at all that is related to cygwin, msys and so forth will |
@@ -74,86 +77,107 @@ void strtof_from_string(char const *st, float &d) { |
74 | 77 | } |
75 | 78 | } |
76 | 79 |
|
77 | | -bool allvalues() { |
| 80 | +// Checks a single 32-bit word (interpreted as a float). Returns true if the |
| 81 | +// parser agrees with the reference, false (after logging) on a mismatch. |
| 82 | +bool check_word(uint32_t word) { |
78 | 83 | char buffer[64]; |
79 | | - for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { |
80 | | - float v; |
81 | | - if ((w % 1048576) == 0) { |
82 | | - std::cout << "."; |
83 | | - std::cout.flush(); |
84 | | - } |
85 | | - uint32_t word = uint32_t(w); |
86 | | - memcpy(&v, &word, sizeof(v)); |
87 | | - if (std::isfinite(v)) { |
88 | | - float nextf = std::nextafterf(v, INFINITY); |
89 | | - if (copysign(1, v) != copysign(1, nextf)) { |
90 | | - continue; |
91 | | - } |
92 | | - if (!std::isfinite(nextf)) { |
93 | | - continue; |
94 | | - } |
95 | | - double v1{v}; |
96 | | - assert(float(v1) == v); |
97 | | - double v2{nextf}; |
98 | | - assert(float(v2) == nextf); |
99 | | - double midv{v1 + (v2 - v1) / 2}; |
100 | | - float expected_midv = float(midv); |
| 84 | + float v; |
| 85 | + memcpy(&v, &word, sizeof(v)); |
| 86 | + if (!std::isfinite(v)) { |
| 87 | + return true; |
| 88 | + } |
| 89 | + float nextf = std::nextafterf(v, INFINITY); |
| 90 | + if (copysign(1, v) != copysign(1, nextf)) { |
| 91 | + return true; |
| 92 | + } |
| 93 | + if (!std::isfinite(nextf)) { |
| 94 | + return true; |
| 95 | + } |
| 96 | + double v1{v}; |
| 97 | + assert(float(v1) == v); |
| 98 | + double v2{nextf}; |
| 99 | + assert(float(v2) == nextf); |
| 100 | + double midv{v1 + (v2 - v1) / 2}; |
| 101 | + float expected_midv = float(midv); |
101 | 102 |
|
102 | | - char const *string_end = to_string(midv, buffer); |
103 | | - float str_answer; |
104 | | - strtof_from_string(buffer, str_answer); |
| 103 | + char const *string_end = to_string(midv, buffer); |
| 104 | + float str_answer; |
| 105 | + strtof_from_string(buffer, str_answer); |
105 | 106 |
|
106 | | - float result_value; |
107 | | - auto result = fast_float::from_chars(buffer, string_end, result_value); |
108 | | - // Starting with version 4.0 for fast_float, we return result_out_of_range |
109 | | - // if the value is either too small (too close to zero) or too large |
110 | | - // (effectively infinity). So std::errc::result_out_of_range is normal for |
111 | | - // well-formed input strings. |
112 | | - if (result.ec != std::errc() && |
113 | | - result.ec != std::errc::result_out_of_range) { |
114 | | - std::cerr << "parsing error ? " << buffer << std::endl; |
115 | | - return false; |
116 | | - } |
117 | | - if (std::isnan(v)) { |
118 | | - if (!std::isnan(result_value)) { |
119 | | - std::cerr << "not nan" << buffer << std::endl; |
120 | | - std::cerr << "v " << std::hexfloat << v << std::endl; |
121 | | - std::cerr << "v2 " << std::hexfloat << v2 << std::endl; |
122 | | - std::cerr << "midv " << std::hexfloat << midv << std::endl; |
123 | | - std::cerr << "expected_midv " << std::hexfloat << expected_midv |
124 | | - << std::endl; |
125 | | - return false; |
126 | | - } |
127 | | - } else if (copysign(1, result_value) != copysign(1, v)) { |
128 | | - std::cerr << buffer << std::endl; |
129 | | - std::cerr << "v " << std::hexfloat << v << std::endl; |
130 | | - std::cerr << "v2 " << std::hexfloat << v2 << std::endl; |
131 | | - std::cerr << "midv " << std::hexfloat << midv << std::endl; |
132 | | - std::cerr << "expected_midv " << std::hexfloat << expected_midv |
133 | | - << std::endl; |
134 | | - std::cerr << "I got " << std::hexfloat << result_value |
135 | | - << " but I was expecting " << v << std::endl; |
136 | | - return false; |
137 | | - } else if (result_value != str_answer) { |
138 | | - std::cerr << "no match ? " << buffer << std::endl; |
139 | | - std::cerr << "v " << std::hexfloat << v << std::endl; |
140 | | - std::cerr << "v2 " << std::hexfloat << v2 << std::endl; |
141 | | - std::cerr << "midv " << std::hexfloat << midv << std::endl; |
142 | | - std::cerr << "expected_midv " << std::hexfloat << expected_midv |
143 | | - << std::endl; |
144 | | - std::cout << "started with " << std::hexfloat << midv << std::endl; |
145 | | - std::cout << "round down to " << std::hexfloat << str_answer |
146 | | - << std::endl; |
147 | | - std::cout << "got back " << std::hexfloat << result_value << std::endl; |
148 | | - std::cout << std::dec; |
149 | | - return false; |
150 | | - } |
| 107 | + float result_value; |
| 108 | + auto result = fast_float::from_chars(buffer, string_end, result_value); |
| 109 | + // Starting with version 4.0 for fast_float, we return result_out_of_range |
| 110 | + // if the value is either too small (too close to zero) or too large |
| 111 | + // (effectively infinity). So std::errc::result_out_of_range is normal for |
| 112 | + // well-formed input strings. |
| 113 | + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { |
| 114 | + std::cerr << "parsing error ? " << buffer << std::endl; |
| 115 | + return false; |
| 116 | + } |
| 117 | + if (std::isnan(v)) { |
| 118 | + if (!std::isnan(result_value)) { |
| 119 | + std::cerr << "not nan" << buffer << std::endl; |
| 120 | + std::cerr << "v " << std::hexfloat << v << std::endl; |
| 121 | + std::cerr << "v2 " << std::hexfloat << v2 << std::endl; |
| 122 | + std::cerr << "midv " << std::hexfloat << midv << std::endl; |
| 123 | + std::cerr << "expected_midv " << std::hexfloat << expected_midv |
| 124 | + << std::endl; |
| 125 | + return false; |
151 | 126 | } |
| 127 | + } else if (copysign(1, result_value) != copysign(1, v)) { |
| 128 | + std::cerr << buffer << std::endl; |
| 129 | + std::cerr << "v " << std::hexfloat << v << std::endl; |
| 130 | + std::cerr << "v2 " << std::hexfloat << v2 << std::endl; |
| 131 | + std::cerr << "midv " << std::hexfloat << midv << std::endl; |
| 132 | + std::cerr << "expected_midv " << std::hexfloat << expected_midv |
| 133 | + << std::endl; |
| 134 | + std::cerr << "I got " << std::hexfloat << result_value |
| 135 | + << " but I was expecting " << v << std::endl; |
| 136 | + return false; |
| 137 | + } else if (result_value != str_answer) { |
| 138 | + std::cerr << "no match ? " << buffer << std::endl; |
| 139 | + std::cerr << "v " << std::hexfloat << v << std::endl; |
| 140 | + std::cerr << "v2 " << std::hexfloat << v2 << std::endl; |
| 141 | + std::cerr << "midv " << std::hexfloat << midv << std::endl; |
| 142 | + std::cerr << "expected_midv " << std::hexfloat << expected_midv |
| 143 | + << std::endl; |
| 144 | + std::cout << "started with " << std::hexfloat << midv << std::endl; |
| 145 | + std::cout << "round down to " << std::hexfloat << str_answer << std::endl; |
| 146 | + std::cout << "got back " << std::hexfloat << result_value << std::endl; |
| 147 | + std::cout << std::dec; |
| 148 | + return false; |
152 | 149 | } |
153 | | - std::cout << std::endl; |
154 | 150 | return true; |
155 | 151 | } |
156 | 152 |
|
| 153 | +// Sweeps the whole 2^32 float space, split across hardware threads (the values |
| 154 | +// are independent). Returns false as soon as any word mismatches. |
| 155 | +bool allvalues() { |
| 156 | + unsigned int nthreads = std::thread::hardware_concurrency(); |
| 157 | + if (nthreads == 0) { |
| 158 | + nthreads = 1; |
| 159 | + } |
| 160 | + std::atomic<bool> ok{true}; |
| 161 | + std::vector<std::thread> workers; |
| 162 | + workers.reserve(nthreads); |
| 163 | + for (unsigned int t = 0; t < nthreads; t++) { |
| 164 | + workers.emplace_back([t, nthreads, &ok]() { |
| 165 | + for (uint64_t w = t; |
| 166 | + w <= 0xFFFFFFFF && ok.load(std::memory_order_relaxed); |
| 167 | + w += nthreads) { |
| 168 | + if (!check_word(uint32_t(w))) { |
| 169 | + ok.store(false, std::memory_order_relaxed); |
| 170 | + return; |
| 171 | + } |
| 172 | + } |
| 173 | + }); |
| 174 | + } |
| 175 | + for (std::thread &worker : workers) { |
| 176 | + worker.join(); |
| 177 | + } |
| 178 | + return ok.load(); |
| 179 | +} |
| 180 | + |
157 | 181 | inline void Assert(bool Assertion) { |
158 | 182 | #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ |
159 | 183 | defined(sun) || defined(__sun) |
|
0 commit comments