101 lines
3.1 KiB
C++
101 lines
3.1 KiB
C++
// Copyright (C) 2020-2024 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along
|
|
// with this library; see the file COPYING3. If not see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
#ifndef ULP_H
|
|
#define ULP_H
|
|
|
|
#include <cmath>
|
|
#include <experimental/simd>
|
|
#include <type_traits>
|
|
#include <cfenv>
|
|
|
|
namespace vir {
|
|
namespace test {
|
|
template <typename T, typename R = typename T::value_type>
|
|
R
|
|
value_type_impl(int);
|
|
|
|
template <typename T>
|
|
T
|
|
value_type_impl(float);
|
|
|
|
template <typename T>
|
|
using value_type_t = decltype(value_type_impl<T>(int()));
|
|
|
|
template <typename T>
|
|
inline T
|
|
ulp_distance(const T& val_, const T& ref_)
|
|
{
|
|
if constexpr (std::is_floating_point_v<value_type_t<T>>)
|
|
{
|
|
const int fp_exceptions = std::fetestexcept(FE_ALL_EXCEPT);
|
|
T val = val_;
|
|
T ref = ref_;
|
|
|
|
T diff = T();
|
|
|
|
using std::abs;
|
|
using std::fpclassify;
|
|
using std::frexp;
|
|
using std::isnan;
|
|
using std::isinf;
|
|
using std::ldexp;
|
|
using std::max;
|
|
using std::experimental::where;
|
|
using TT = value_type_t<T>;
|
|
|
|
where(ref == 0, val) = abs(val);
|
|
where(ref == 0, diff) = 1;
|
|
where(ref == 0, ref) = std::__norm_min_v<TT>;
|
|
where(isinf(ref) && ref == val, ref)
|
|
= 0; // where(val_ == ref_) = 0 below will fix it up
|
|
|
|
where(val == 0, ref) = abs(ref);
|
|
where(val == 0, diff) += 1;
|
|
where(val == 0, val) = std::__norm_min_v<TT>;
|
|
|
|
using I = decltype(fpclassify(std::declval<T>()));
|
|
I exp = {};
|
|
frexp(ref, &exp);
|
|
// lower bound for exp must be min_exponent to scale the resulting
|
|
// difference from a denormal correctly
|
|
exp = max(exp, I(std::__min_exponent_v<TT>));
|
|
diff += ldexp(abs(ref - val), std::__digits_v<TT> - exp);
|
|
where(val_ == ref_ || (isnan(val_) && isnan(ref_)), diff) = T();
|
|
std::feclearexcept(FE_ALL_EXCEPT ^ fp_exceptions);
|
|
return diff;
|
|
}
|
|
else
|
|
{
|
|
if (val_ > ref_)
|
|
return val_ - ref_;
|
|
else
|
|
return ref_ - val_;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
inline T
|
|
ulp_distance_signed(const T& _val, const T& _ref)
|
|
{
|
|
using std::copysign;
|
|
return copysign(ulp_distance(_val, _ref), _val - _ref);
|
|
}
|
|
} // namespace test
|
|
} // namespace vir
|
|
|
|
#endif // ULP_H
|