From 01ad085ac204fb56a4191cdc1ba622f43511ebd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 21 Aug 2020 23:24:58 +0200 Subject: [PATCH 1/4] Add roundeven experiment --- test/CMakeLists.txt | 1 + test/exhaustive/CMakeLists.txt | 4 ++ test/exhaustive/roundeven_test.cpp | 79 ++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 test/exhaustive/CMakeLists.txt create mode 100644 test/exhaustive/roundeven_test.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e1dd301c3..75bcf5704 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,7 @@ set(fizzy_include_dir ${PROJECT_SOURCE_DIR}/lib/fizzy) add_subdirectory(utils) add_subdirectory(bench) add_subdirectory(bench_internal) +add_subdirectory(exhaustive) add_subdirectory(spectests) add_subdirectory(unittests) add_subdirectory(smoketests) diff --git a/test/exhaustive/CMakeLists.txt b/test/exhaustive/CMakeLists.txt new file mode 100644 index 000000000..6b28dc836 --- /dev/null +++ b/test/exhaustive/CMakeLists.txt @@ -0,0 +1,4 @@ + + +add_executable(test-roundeven roundeven_test.cpp) +target_link_libraries(test-roundeven PRIVATE fizzy::test-utils) diff --git a/test/exhaustive/roundeven_test.cpp b/test/exhaustive/roundeven_test.cpp new file mode 100644 index 000000000..e661f80d7 --- /dev/null +++ b/test/exhaustive/roundeven_test.cpp @@ -0,0 +1,79 @@ +#include +#include + +using fizzy::test::FP; + + +float my_nearest(float x) +{ + const auto u = FP{x}.as_uint(); + + if (std::isnan(x)) + return FP{u | 0x400000}.value; + + const auto t = std::trunc(x); + + if (x == t) + return t; + + const auto is_even = std::fmod(t, 2.0f) == 0; + + + if (std::signbit(x) == 0) // positive + { + const auto diff = x - t; + + if (diff < 0.5f) + return t; + + if (diff > 0.5f) + return t + 1; + + + + if (is_even) + return t; + else + return t + 1; + } + else + { + const auto diff = t - x; + + if (diff < 0.5f) + return t; + + if (diff > 0.5f) + return t - 1; + + if (is_even) + return t; + else + return t - 1; + } +} + +int main() +{ + uint32_t i = 0; + do + { + const auto value = FP{i}.value; + const auto n = my_nearest(value); + const auto expected = ::roundevenf(value); + + if (FP{n} != FP{expected}) + { + std::cout << value << " " << n << " " << expected << "\n"; + std::cout << std::hexfloat << value << " " << n << " " << expected << "\n"; + std::cout << std::hex << FP{value}.as_uint() << " " << FP{n}.as_uint() << " " + << FP{expected}.as_uint() << "\n"; + + std::cout << FP{n}.nan_payload() << " " << FP{expected}.nan_payload(); + return 1; + } + ++i; + } while (i != 0); + + return 0; +} From 21afe0337c3e0ab1e741e9d94c8a215ef15220e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sun, 23 Aug 2020 16:31:46 +0200 Subject: [PATCH 2/4] Optimize --- test/exhaustive/roundeven_test.cpp | 63 ++++++++++++++---------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/test/exhaustive/roundeven_test.cpp b/test/exhaustive/roundeven_test.cpp index e661f80d7..405ac41ba 100644 --- a/test/exhaustive/roundeven_test.cpp +++ b/test/exhaustive/roundeven_test.cpp @@ -1,60 +1,51 @@ #include +#include #include using fizzy::test::FP; - -float my_nearest(float x) +template +static bool is_even(T x) { + using FP = FP; const auto u = FP{x}.as_uint(); - if (std::isnan(x)) - return FP{u | 0x400000}.value; - - const auto t = std::trunc(x); - - if (x == t) - return t; + const auto m = u & FP::mantissa_mask; - const auto is_even = std::fmod(t, 2.0f) == 0; + constexpr auto exponent_mask = (typename FP::UintType{1} << FP::num_exponent_bits) - 1; + constexpr auto exponent_bias = (typename FP::UintType{1} << (FP::num_exponent_bits - 1)) - 1; + const auto e = ((u >> FP::num_mantissa_bits) & exponent_mask) - exponent_bias; - if (std::signbit(x) == 0) // positive - { - const auto diff = x - t; + if (e == 0) // 1 + return false; - if (diff < 0.5f) - return t; + const auto lowest_bit = FP::num_mantissa_bits - e; - if (diff > 0.5f) - return t + 1; + return ((m >> lowest_bit) & 1) == 0; +} +float my_nearest(float x) +{ + if (std::isnan(x)) + return FP{FP{x}.as_uint() | 0x400000}.value; - if (is_even) - return t; - else - return t + 1; - } - else - { - const auto diff = t - x; + auto t = std::trunc(x); - if (diff < 0.5f) - return t; + const auto diff = std::abs(x - t); - if (diff > 0.5f) - return t - 1; + if (diff > 0.5f || (diff == 0.5f && !is_even(t))) + t = t + std::copysign(1.0f, x); - if (is_even) - return t; - else - return t - 1; - } + return t; } int main() { + using clock = std::chrono::steady_clock; + const auto start_time = clock::now(); + uint32_t i = 0; do { @@ -75,5 +66,9 @@ int main() ++i; } while (i != 0); + std::cout + << "time: " + << std::chrono::duration_cast(clock::now() - start_time).count() + << " ms\n"; return 0; } From 289c234b93aa61f7a5df5994ff4b7df3267f9b41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sun, 23 Aug 2020 18:03:00 +0200 Subject: [PATCH 3/4] Simple roundeven() --- test/exhaustive/roundeven_test.cpp | 31 ++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/test/exhaustive/roundeven_test.cpp b/test/exhaustive/roundeven_test.cpp index 405ac41ba..6d085eef6 100644 --- a/test/exhaustive/roundeven_test.cpp +++ b/test/exhaustive/roundeven_test.cpp @@ -41,6 +41,18 @@ float my_nearest(float x) return t; } + +float roundeven_simple(float x) +{ + static constexpr auto is_even = [](auto i) noexcept { return std::fmod(i, 2.0f) == 0; }; + + const auto t = std::trunc(x); + if (const auto diff = std::abs(x - t); diff > 0.5f || (diff == 0.5f && !is_even(t))) + return t + std::copysign(1.0f, x); + else + return t; +} + int main() { using clock = std::chrono::steady_clock; @@ -50,18 +62,21 @@ int main() do { const auto value = FP{i}.value; - const auto n = my_nearest(value); + const auto n = roundeven_simple(value); const auto expected = ::roundevenf(value); if (FP{n} != FP{expected}) { - std::cout << value << " " << n << " " << expected << "\n"; - std::cout << std::hexfloat << value << " " << n << " " << expected << "\n"; - std::cout << std::hex << FP{value}.as_uint() << " " << FP{n}.as_uint() << " " - << FP{expected}.as_uint() << "\n"; - - std::cout << FP{n}.nan_payload() << " " << FP{expected}.nan_payload(); - return 1; + if (!std::isnan(n) || !std::isnan(expected)) + { + std::cout << value << " " << n << " " << expected << "\n"; + std::cout << std::hexfloat << value << " " << n << " " << expected << "\n"; + std::cout << std::hex << FP{value}.as_uint() << " " << FP{n}.as_uint() << " " + << FP{expected}.as_uint() << "\n"; + + std::cout << FP{n}.nan_payload() << " " << FP{expected}.nan_payload(); + return 1; + } } ++i; } while (i != 0); From 8876df46649a04f2a9f7be879da0a8efcaca8cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sun, 23 Aug 2020 18:24:05 +0200 Subject: [PATCH 4/4] Test all rounding directions --- test/exhaustive/roundeven_test.cpp | 40 +++++++++++++++++------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/test/exhaustive/roundeven_test.cpp b/test/exhaustive/roundeven_test.cpp index 6d085eef6..f486941fa 100644 --- a/test/exhaustive/roundeven_test.cpp +++ b/test/exhaustive/roundeven_test.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -58,28 +59,33 @@ int main() using clock = std::chrono::steady_clock; const auto start_time = clock::now(); - uint32_t i = 0; - do + for (const auto rounding_direction : {FE_TONEAREST, FE_DOWNWARD, FE_UPWARD, FE_TOWARDZERO}) { - const auto value = FP{i}.value; - const auto n = roundeven_simple(value); - const auto expected = ::roundevenf(value); + std::fesetround(rounding_direction); - if (FP{n} != FP{expected}) + uint32_t i = 0; + do { - if (!std::isnan(n) || !std::isnan(expected)) - { - std::cout << value << " " << n << " " << expected << "\n"; - std::cout << std::hexfloat << value << " " << n << " " << expected << "\n"; - std::cout << std::hex << FP{value}.as_uint() << " " << FP{n}.as_uint() << " " - << FP{expected}.as_uint() << "\n"; + const auto value = FP{i}.value; + const auto n = roundeven_simple(value); + const auto expected = ::roundevenf(value); - std::cout << FP{n}.nan_payload() << " " << FP{expected}.nan_payload(); - return 1; + if (FP{n} != FP{expected}) + { + if (!std::isnan(n) || !std::isnan(expected)) + { + std::cout << value << " " << n << " " << expected << "\n"; + std::cout << std::hexfloat << value << " " << n << " " << expected << "\n"; + std::cout << std::hex << FP{value}.as_uint() << " " << FP{n}.as_uint() << " " + << FP{expected}.as_uint() << "\n"; + + std::cout << FP{n}.nan_payload() << " " << FP{expected}.nan_payload(); + return 1; + } } - } - ++i; - } while (i != 0); + ++i; + } while (i != 0); + } std::cout << "time: "