|
13 | 13 | #include "cxxopts.hpp" |
14 | 14 | #include "random_generators.h" |
15 | 15 |
|
| 16 | +#include <cassert> |
16 | 17 | #include <climits> |
17 | 18 | #include <cmath> |
18 | 19 | #include <cstdio> |
|
24 | 25 | using Benchmarks::arithmetic_float; |
25 | 26 | using Benchmarks::BenchArgs; |
26 | 27 |
|
| 28 | +template <arithmetic_float T> |
| 29 | +void evaluateProperties(const std::vector<T> &lines, |
| 30 | + const std::array<BenchArgs<T>, Benchmarks::COUNT> &args) { |
| 31 | + constexpr auto precision = std::numeric_limits<T>::digits10; |
| 32 | + fmt::println("{:20} {:20}", "Algorithm", "Valid round-trip"); |
| 33 | + |
| 34 | + for (const auto &algo : args) { |
| 35 | + if (!algo.used) { |
| 36 | + std::cout << "# skipping " << algo.name << std::endl; |
| 37 | + continue; |
| 38 | + } |
| 39 | + |
| 40 | + char buf1[100], buf2[100]; |
| 41 | + std::span<char> bufRef(buf1, sizeof(buf1)), bufAlgo(buf2, sizeof(buf2)); |
| 42 | + int incorrect = 0; |
| 43 | + for (const auto d : lines) { |
| 44 | + // Reference output |
| 45 | + const int vRef = Benchmarks::std_to_chars(d, bufRef); |
| 46 | + bufRef[vRef] = '\0'; |
| 47 | + T dRef; |
| 48 | + auto [ptr, ec] = std::from_chars(bufRef.data(), bufRef.data() + vRef, dRef); |
| 49 | + assert(ptr == bufRef.data() + vRef); |
| 50 | + assert(ec == std::errc()); |
| 51 | + assert(d == dRef); |
| 52 | + |
| 53 | + // Tested algorithm output |
| 54 | + const int vAlgo = algo.func(d, bufAlgo); |
| 55 | + bufAlgo[vAlgo] = '\0'; |
| 56 | + T dAlgo; |
| 57 | + auto [ptrAlgo, ecAlgo] = std::from_chars(bufAlgo.data(), bufAlgo.data() + vAlgo, dAlgo); |
| 58 | + assert(ptrAlgo == bufAlgo.data() + vAlgo); |
| 59 | + assert(ecAlgo == std::errc()); |
| 60 | + if ((incorrect += (d != dAlgo)) == 1) |
| 61 | + fmt::println("\t{:20} mismatch: d = {:.17f}, bufRef = {}, bufAlgo = {}, dAlgo = {:.17f}", |
| 62 | + algo.name, d, bufRef.data(), bufAlgo.data(), dAlgo); |
| 63 | + } |
| 64 | + fmt::println("{:20} {:20}", algo.name, incorrect == 0 ? "yes" : "no"); |
| 65 | + } |
| 66 | +} |
| 67 | + |
27 | 68 | template <arithmetic_float T> |
28 | 69 | void process(const std::vector<T> &lines, |
29 | 70 | const std::array<BenchArgs<T>, Benchmarks::COUNT> &args) { |
30 | | - auto testWrapper = [](const std::vector<T> &lines, const std::string &name, |
31 | | - int (*func)(T, std::span<char> &), int repeat = 1) { |
32 | | - pretty_print(lines, name, [func, repeat](const std::vector<T> &lines) -> int { |
33 | | - int volume = 0; |
34 | | - char buf[100]; |
35 | | - std::span<char> bufspan(buf, sizeof(buf)); |
36 | | - for (const auto d : lines) |
37 | | - volume += func(d, bufspan); |
38 | | - return volume; |
39 | | - }, repeat); |
40 | | - }; |
41 | | - |
42 | 71 | for (const auto &algo : args) { |
43 | 72 | if (!algo.used) { |
44 | 73 | std::cout << "# skipping " << algo.name << std::endl; |
45 | 74 | continue; |
46 | 75 | } |
47 | | - testWrapper(lines, algo.name, algo.func, algo.testRepeat); |
| 76 | + |
| 77 | + pretty_print(lines, algo.name, [&algo](const std::vector<T> &lines) -> int { |
| 78 | + int volume = 0; |
| 79 | + char buf[100]; |
| 80 | + std::span<char> bufspan(buf, sizeof(buf)); |
| 81 | + for (const auto d : lines) |
| 82 | + volume += algo.func(d, bufspan); |
| 83 | + return volume; |
| 84 | + }, algo.testRepeat); |
48 | 85 | } |
49 | 86 | } |
50 | 87 |
|
@@ -103,9 +140,11 @@ int main(int argc, char **argv) { |
103 | 140 | cxxopts::value<std::string>()->default_value("uniform"))( |
104 | 141 | "s,single", "Use single precision instead of double.", |
105 | 142 | cxxopts::value<bool>()->default_value("false"))( |
106 | | - "d,dragon", "Enable dragon4 (current impl. triggers some asserts)", |
| 143 | + "t,test", "Test the algorithms and find their properties.", |
| 144 | + cxxopts::value<bool>()->default_value("false"))( |
| 145 | + "d,dragon", "Enable dragon4 (current impl. triggers some asserts).", |
107 | 146 | cxxopts::value<bool>()->default_value("false"))( |
108 | | - "e,errol", "Enable errol3 (current impl. returns invalid values, e.g., for 0)", |
| 147 | + "e,errol", "Enable errol3 (current impl. returns invalid values, e.g., for 0).", |
109 | 148 | cxxopts::value<bool>()->default_value("false"))( |
110 | 149 | "h,help", "Print usage."); |
111 | 150 | const auto result = options.parse(argc, argv); |
@@ -167,11 +206,16 @@ int main(int argc, char **argv) { |
167 | 206 | else |
168 | 207 | algorithms = initArgs(double{}); |
169 | 208 |
|
170 | | - std::visit([](const auto &lines, const auto &args) { |
| 209 | + const bool test = result["test"].as<bool>(); |
| 210 | + std::visit([test](const auto &lines, const auto &args) { |
171 | 211 | using T1 = typename std::decay_t<decltype(lines)>::value_type; |
172 | 212 | using T2 = typename std::decay_t<decltype(args)>::value_type::Type; |
173 | | - if constexpr (std::is_same_v<T1, T2>) |
174 | | - process(lines, args); |
| 213 | + if constexpr (std::is_same_v<T1, T2>) { |
| 214 | + if (test) |
| 215 | + evaluateProperties(lines, args); |
| 216 | + else |
| 217 | + process(lines, args); |
| 218 | + } |
175 | 219 | }, numbers, algorithms); |
176 | 220 | } catch (const std::exception &e) { |
177 | 221 | std::cout << "error parsing options: " << e.what() << std::endl; |
|
0 commit comments