|
8 | 8 | #include <iostream> |
9 | 9 | #include <limits> |
10 | 10 | #include <system_error> |
| 11 | +#include <thread> |
| 12 | +#include <vector> |
11 | 13 |
|
12 | 14 | template <typename T> char *to_string(T d, char *buffer) { |
13 | 15 | auto written = std::snprintf(buffer, 64, "%.*e", |
14 | 16 | std::numeric_limits<T>::max_digits10 - 1, d); |
15 | 17 | return buffer + written; |
16 | 18 | } |
17 | 19 |
|
18 | | -void allvalues() { |
| 20 | +// Checks a single 32-bit word (interpreted as a float); aborts on a mismatch. |
| 21 | +void check_word(uint32_t word) { |
19 | 22 | char buffer[64]; |
20 | | - for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { |
21 | | - float v; |
22 | | - if ((w % 1048576) == 0) { |
23 | | - std::cout << "."; |
24 | | - std::cout.flush(); |
| 23 | + float v; |
| 24 | + memcpy(&v, &word, sizeof(v)); |
| 25 | + |
| 26 | + char const *string_end = to_string(v, buffer); |
| 27 | + float result_value; |
| 28 | + auto result = fast_float::from_chars(buffer, string_end, result_value); |
| 29 | + // Starting with version 4.0 for fast_float, we return result_out_of_range |
| 30 | + // if the value is either too small (too close to zero) or too large |
| 31 | + // (effectively infinity). So std::errc::result_out_of_range is normal for |
| 32 | + // well-formed input strings. |
| 33 | + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { |
| 34 | + std::cerr << "parsing error ? " << buffer << std::endl; |
| 35 | + abort(); |
| 36 | + } |
| 37 | + if (std::isnan(v)) { |
| 38 | + if (!std::isnan(result_value)) { |
| 39 | + std::cerr << "not nan" << buffer << std::endl; |
| 40 | + abort(); |
25 | 41 | } |
26 | | - uint32_t word = uint32_t(w); |
27 | | - memcpy(&v, &word, sizeof(v)); |
| 42 | + } else if (copysign(1, result_value) != copysign(1, v)) { |
| 43 | + std::cerr << "I got " << std::hexfloat << result_value |
| 44 | + << " but I was expecting " << v << std::endl; |
| 45 | + abort(); |
| 46 | + } else if (result_value != v) { |
| 47 | + std::cerr << "no match ? " << buffer << std::endl; |
| 48 | + std::cout << "started with " << std::hexfloat << v << std::endl; |
| 49 | + std::cout << "got back " << std::hexfloat << result_value << std::endl; |
| 50 | + std::cout << std::dec; |
| 51 | + abort(); |
| 52 | + } |
| 53 | +} |
28 | 54 |
|
29 | | - { |
30 | | - char const *string_end = to_string(v, buffer); |
31 | | - float result_value; |
32 | | - auto result = fast_float::from_chars(buffer, string_end, result_value); |
33 | | - // Starting with version 4.0 for fast_float, we return result_out_of_range |
34 | | - // if the value is either too small (too close to zero) or too large |
35 | | - // (effectively infinity). So std::errc::result_out_of_range is normal for |
36 | | - // well-formed input strings. |
37 | | - if (result.ec != std::errc() && |
38 | | - result.ec != std::errc::result_out_of_range) { |
39 | | - std::cerr << "parsing error ? " << buffer << std::endl; |
40 | | - abort(); |
41 | | - } |
42 | | - if (std::isnan(v)) { |
43 | | - if (!std::isnan(result_value)) { |
44 | | - std::cerr << "not nan" << buffer << std::endl; |
45 | | - abort(); |
46 | | - } |
47 | | - } else if (copysign(1, result_value) != copysign(1, v)) { |
48 | | - std::cerr << "I got " << std::hexfloat << result_value |
49 | | - << " but I was expecting " << v << std::endl; |
50 | | - abort(); |
51 | | - } else if (result_value != v) { |
52 | | - std::cerr << "no match ? " << buffer << std::endl; |
53 | | - std::cout << "started with " << std::hexfloat << v << std::endl; |
54 | | - std::cout << "got back " << std::hexfloat << result_value << std::endl; |
55 | | - std::cout << std::dec; |
56 | | - abort(); |
| 55 | +// Sweeps the whole 2^32 float space, split across hardware threads (the values |
| 56 | +// are independent); check_word() aborts on the first mismatch. |
| 57 | +void allvalues() { |
| 58 | + unsigned int nthreads = std::thread::hardware_concurrency(); |
| 59 | + if (nthreads == 0) { |
| 60 | + nthreads = 1; |
| 61 | + } |
| 62 | + std::vector<std::thread> workers; |
| 63 | + workers.reserve(nthreads); |
| 64 | + for (unsigned int t = 0; t < nthreads; t++) { |
| 65 | + workers.emplace_back([t, nthreads]() { |
| 66 | + for (uint64_t w = t; w <= 0xFFFFFFFF; w += nthreads) { |
| 67 | + check_word(uint32_t(w)); |
57 | 68 | } |
58 | | - } |
| 69 | + }); |
| 70 | + } |
| 71 | + for (std::thread &worker : workers) { |
| 72 | + worker.join(); |
59 | 73 | } |
60 | 74 | std::cout << std::endl; |
61 | 75 | } |
|
0 commit comments