1 #ifndef MARNAV_MATH_FLOATINGPONIT_ULPS_HPP
2 #define MARNAV_MATH_FLOATINGPONIT_ULPS_HPP
4 // implementation based on https://bitbashing.io/comparing-floats.html
5 // by matt <at> bitbashing.io
7 // this file is distributed under following license: CC BY-SA 4.0
8 // https://creativecommons.org/licenses/by-sa/4.0/
22 template <class TF
> struct corresponding_integer
{};
23 template <> struct corresponding_integer
<float> { using type
= int32_t; };
24 template <> struct corresponding_integer
<double> { using type
= int64_t; };
27 using corresponding_integer_t
= typename corresponding_integer
<TF
>::type
;
29 template <class TF
> struct default_ulps
{};
30 template <> struct default_ulps
<float> { static constexpr corresponding_integer_t
<float> value
= 1e4
; };
31 template <> struct default_ulps
<double> { static constexpr corresponding_integer_t
<double> value
= 1e6
; };
34 template <class TF
, class TI
= corresponding_integer_t
<TF
>>
35 TI
ulps_distance(const TF a
, const TF b
)
37 // save work if the floats are equal, also handles +0 == -0
41 // max distance for NaN
42 if (std::isnan(a
) || std::isnan(b
))
43 return std::numeric_limits
<TI
>::max();
45 // max distance for INF
46 if (std::isinf(a
) || std::isinf(b
))
47 return std::numeric_limits
<TI
>::max();
51 ::memcpy(&ia
, &a
, sizeof(TF
));
52 ::memcpy(&ib
, &b
, sizeof(TF
));
54 // don't compare differently-signed floats.
55 if ((ia
< 0) != (ib
< 0))
56 return std::numeric_limits
<TI
>::max();
58 // return the absolute value of the distance in ULPs.
59 const TI distance
= ia
- ib
;
60 return (distance
< 0) ? -distance
: distance
;
64 template <class TF
, class TI
= detail::corresponding_integer_t
<TF
>>
65 bool nearly_equal(TF a
, TF b
, TF fixed_epsilon
= std::numeric_limits
<TF
>::epsilon(),
66 TI ulps_epsilon
= detail::default_ulps
<TF
>::value
)
68 const auto difference
= std::abs(a
- b
);
69 if (difference
<= fixed_epsilon
)
71 return detail::ulps_distance(a
, b
) <= ulps_epsilon
;