Skip to content

Commit b642d92

Browse files
tests: parallelize exhaustive32 and exhaustive32_64 sweeps too
Same std::thread split as exhaustive32_midpoint; preserves each test's existing failure behavior (abort for exhaustive32, stop-flag for exhaustive32_64).
1 parent b20c420 commit b642d92

2 files changed

Lines changed: 83 additions & 53 deletions

File tree

tests/exhaustive32.cpp

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,54 +8,68 @@
88
#include <iostream>
99
#include <limits>
1010
#include <system_error>
11+
#include <thread>
12+
#include <vector>
1113

1214
template <typename T> char *to_string(T d, char *buffer) {
1315
auto written = std::snprintf(buffer, 64, "%.*e",
1416
std::numeric_limits<T>::max_digits10 - 1, d);
1517
return buffer + written;
1618
}
1719

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) {
1922
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();
2541
}
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+
}
2854

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));
5768
}
58-
}
69+
});
70+
}
71+
for (std::thread &worker : workers) {
72+
worker.join();
5973
}
6074
std::cout << std::endl;
6175
}

tests/exhaustive32_64.cpp

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
#include "fast_float/fast_float.h"
33

4+
#include <atomic>
45
#include <cassert>
56
#include <cmath>
67
#include <cstdio>
@@ -9,6 +10,8 @@
910
#include <limits>
1011
#include <string>
1112
#include <system_error>
13+
#include <thread>
14+
#include <vector>
1215

1316
template <typename T> char *to_string(T d, char *buffer) {
1417
auto written = std::snprintf(buffer, 64, "%.*e",
@@ -45,25 +48,38 @@ bool basic_test_64bit(std::string vals, double val) {
4548
return true;
4649
}
4750

51+
// Sweeps the whole 2^32 float space (widened to double), split across hardware
52+
// threads (the values are independent); stops at the first mismatch.
4853
void all_32bit_values() {
49-
char buffer[64];
50-
for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) {
51-
float v32;
52-
if ((w % 1048576) == 0) {
53-
std::cout << ".";
54-
std::cout.flush();
55-
}
56-
uint32_t word = uint32_t(w);
57-
memcpy(&v32, &word, sizeof(v32));
58-
double v = v32;
54+
unsigned int nthreads = std::thread::hardware_concurrency();
55+
if (nthreads == 0) {
56+
nthreads = 1;
57+
}
58+
std::atomic<bool> ok{true};
59+
std::vector<std::thread> workers;
60+
workers.reserve(nthreads);
61+
for (unsigned int t = 0; t < nthreads; t++) {
62+
workers.emplace_back([t, nthreads, &ok]() {
63+
char buffer[64];
64+
for (uint64_t w = t;
65+
w <= 0xFFFFFFFF && ok.load(std::memory_order_relaxed);
66+
w += nthreads) {
67+
float v32;
68+
uint32_t word = uint32_t(w);
69+
memcpy(&v32, &word, sizeof(v32));
70+
double v = v32;
5971

60-
{
61-
char const *string_end = to_string(v, buffer);
62-
std::string s(buffer, size_t(string_end - buffer));
63-
if (!basic_test_64bit(s, v)) {
64-
return;
72+
char const *string_end = to_string(v, buffer);
73+
std::string s(buffer, size_t(string_end - buffer));
74+
if (!basic_test_64bit(s, v)) {
75+
ok.store(false, std::memory_order_relaxed);
76+
return;
77+
}
6578
}
66-
}
79+
});
80+
}
81+
for (std::thread &worker : workers) {
82+
worker.join();
6783
}
6884
std::cout << std::endl;
6985
}

0 commit comments

Comments
 (0)