Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Change implementation of fnearest()
Browse files Browse the repository at this point in the history
This implementation does not need to modify rounding direction to work
correctly under any rounding direction.
chfast committed Aug 23, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 744a469 commit 991b4db
Showing 1 changed file with 6 additions and 28 deletions.
34 changes: 6 additions & 28 deletions lib/fizzy/execute.cpp
Original file line number Diff line number Diff line change
@@ -10,7 +10,6 @@
#include "types.hpp"
#include <algorithm>
#include <cassert>
#include <cfenv>
#include <cmath>
#include <cstring>
#include <stack>
@@ -590,34 +589,13 @@ T fnearest(T value) noexcept
if (std::isnan(value))
return std::numeric_limits<T>::quiet_NaN(); // Positive canonical NaN.

// This implementation uses std::nearbyint() which rounds to integer using current rounding
// direction. The rounding direction is temporarily set to "to nearest" for the case when
// the current rounding direction is different.
static constexpr auto is_even = [](auto i) noexcept { return std::fmod(i, 2.0f) == 0; };

// The future C standard has roundeven() function which is much better for the job.
// But not generally available yet. Glibc has it already. GCC 10 has __builtin_roundeven().
// The LLVM has @llvm.roundeven intrinsic, but seems not to be exposed in Clang yet.

// Save the current rounding direction. This may be invalid negative value meaning a failure
// (probably due to changing rounding direction not being supported on the platform).
// volatile is used to prevent calls reordering by the compiler.
const volatile auto current_rounding_direction = std::fegetround();

// Temporarily change the rounding direction to "to nearest" if needed.
if (current_rounding_direction != FE_TONEAREST)
{
[[maybe_unused]] const auto set_succeeded = std::fesetround(FE_TONEAREST);
assert(set_succeeded == 0);
}

// volatile is used to prevent compiler from moving the last std::fesetround() before.
const volatile auto result = std::nearbyint(value);

// Reset the rounding direction if we know the previous value and it was actually modified.
if (current_rounding_direction >= 0 && current_rounding_direction != FE_TONEAREST)
std::fesetround(current_rounding_direction);

return result;
const auto t = std::trunc(value);
if (const auto diff = std::abs(value - t); diff > 0.5f || (diff == 0.5f && !is_even(t)))
return t + std::copysign(1.0f, value);
else
return t;
}

template <typename T>

0 comments on commit 991b4db

Please sign in to comment.