libuav original

Dependents:   UAVCAN UAVCAN_Subscriber

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers comparison.hpp Source File

comparison.hpp

00001 /*
00002  * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
00003  */
00004 
00005 #ifndef UAVCAN_UTIL_COMPARISON_HPP_INCLUDED
00006 #define UAVCAN_UTIL_COMPARISON_HPP_INCLUDED
00007 
00008 #include <uavcan/util/templates.hpp>
00009 #include <uavcan/build_config.hpp>
00010 
00011 namespace uavcan
00012 {
00013 /**
00014  * Exact comparison of two floats that suppresses the compiler warnings.
00015  */
00016 template <typename T>
00017 UAVCAN_EXPORT
00018 inline bool areFloatsExactlyEqual(const T& left, const T& right)
00019 {
00020     return (left <= right) && (left >= right);
00021 }
00022 
00023 /**
00024  * This function performs fuzzy comparison of two floating point numbers.
00025  * Type of T can be either float, double or long double.
00026  * For details refer to http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
00027  * See also: @ref UAVCAN_FLOAT_COMPARISON_EPSILON_MULT.
00028  */
00029 template <typename T>
00030 UAVCAN_EXPORT
00031 inline bool areFloatsClose(T a, T b, const T& absolute_epsilon, const T& relative_epsilon)
00032 {
00033     // NAN
00034     if (isNaN(a) || isNaN(b))
00035     {
00036         return false;
00037     }
00038 
00039     // Infinity
00040     if (isInfinity(a) || isInfinity(b))
00041     {
00042         return areFloatsExactlyEqual(a, b);
00043     }
00044 
00045     // Close numbers near zero
00046     const T diff = std::fabs(a - b);
00047     if (diff <= absolute_epsilon)
00048     {
00049         return true;
00050     }
00051 
00052     // General case
00053     a = std::fabs(a);
00054     b = std::fabs(b);
00055     const T largest = (b > a) ? b : a;
00056     return (diff <= largest * relative_epsilon);
00057 }
00058 
00059 /**
00060  * This namespace contains implementation details for areClose().
00061  * Don't try this at home.
00062  */
00063 namespace are_close_impl_
00064 {
00065 
00066 struct Applicable { char foo[1]; };
00067 struct NotApplicable { long long foo[16]; };
00068 
00069 template <typename This, typename Rhs>
00070 struct HasIsCloseMethod
00071 {
00072     template <typename U, typename R, bool (U::*)(const R&) const> struct ConstRef { };
00073     template <typename U, typename R, bool (U::*)(R) const> struct ByValue { };
00074 
00075     template <typename U, typename R> static Applicable test(ConstRef<U, R, &U::isClose>*);
00076     template <typename U, typename R> static Applicable test(ByValue<U, R, &U::isClose>*);
00077 
00078     template <typename U, typename R> static NotApplicable test(...);
00079 
00080     enum { Result = sizeof(test<This, Rhs>(UAVCAN_NULLPTR)) };
00081 };
00082 
00083 /// First stage: bool L::isClose(R)
00084 template <typename L, typename R>
00085 UAVCAN_EXPORT
00086 inline bool areCloseImplFirst(const L& left, const R& right, IntToType<sizeof(Applicable)>)
00087 {
00088     return left.isClose(right);
00089 }
00090 
00091 /// Second stage: bool R::isClose(L)
00092 template <typename L, typename R>
00093 UAVCAN_EXPORT
00094 inline bool areCloseImplSecond(const L& left, const R& right, IntToType<sizeof(Applicable)>)
00095 {
00096     return right.isClose(left);
00097 }
00098 
00099 /// Second stage: L == R
00100 template <typename L, typename R>
00101 UAVCAN_EXPORT
00102 inline bool areCloseImplSecond(const L& left, const R& right, IntToType<sizeof(NotApplicable)>)
00103 {
00104     return left == right;
00105 }
00106 
00107 /// First stage: select either L == R or bool R::isClose(L)
00108 template <typename L, typename R>
00109 UAVCAN_EXPORT
00110 inline bool areCloseImplFirst(const L& left, const R& right, IntToType<sizeof(NotApplicable)>)
00111 {
00112     return are_close_impl_::areCloseImplSecond(left, right,
00113                                                IntToType<are_close_impl_::HasIsCloseMethod<R, L>::Result>());
00114 }
00115 
00116 } // namespace are_close_impl_
00117 
00118 /**
00119  * Generic fuzzy comparison function.
00120  *
00121  * This function properly handles floating point comparison, including mixed floating point type comparison,
00122  * e.g. float with long double.
00123  *
00124  * Two objects of types A and B will be fuzzy comparable if either method is defined:
00125  *  - bool A::isClose(const B&) const
00126  *  - bool A::isClose(B) const
00127  *  - bool B::isClose(const A&) const
00128  *  - bool B::isClose(A) const
00129  *
00130  * Call areClose(A, B) will be dispatched as follows:
00131  *
00132  *  - If A and B are both floating point types (float, double, long double) - possibly different - the call will be
00133  *    dispatched to @ref areFloatsClose(). If A and B are different types, value of the larger type will be coerced
00134  *    to the smaller type, e.g. areClose(long double, float) --> areClose(float, float).
00135  *
00136  *  - If A defines isClose() that accepts B, the call will be dispatched there.
00137  *
00138  *  - If B defines isClose() that accepts A, the call will be dispatched there (A/B swapped).
00139  *
00140  *  - Last resort is A == B.
00141  *
00142  * Alternatively, a custom behavior can be implemented via template specialization.
00143  *
00144  * See also: @ref UAVCAN_FLOAT_COMPARISON_EPSILON_MULT.
00145  *
00146  * Examples:
00147  *  areClose(1.0, 1.0F)                                         --> true
00148  *  areClose(1.0, 1.0F + std::numeric_limits<float>::epsilon()) --> true
00149  *  areClose(1.0, 1.1)                                          --> false
00150  *  areClose("123", std::string("123"))                         --> true (using std::string's operator ==)
00151  *  areClose(inf, inf)                                          --> true
00152  *  areClose(inf, -inf)                                         --> false
00153  *  areClose(nan, nan)                                          --> false (any comparison with nan returns false)
00154  *  areClose(123, "123")                                        --> compilation error: operator == is not defined
00155  */
00156 template <typename L, typename R>
00157 UAVCAN_EXPORT
00158 inline bool areClose(const L& left, const R& right)
00159 {
00160     return are_close_impl_::areCloseImplFirst(left, right,
00161                                               IntToType<are_close_impl_::HasIsCloseMethod<L, R>::Result>());
00162 }
00163 
00164 /*
00165  * Float comparison specializations
00166  */
00167 template <>
00168 UAVCAN_EXPORT
00169 inline bool areClose<float, float>(const float& left, const float& right)
00170 {
00171     return areFloatsClose(left, right, NumericTraits<float>::epsilon(),
00172                           NumericTraits<float>::epsilon() * FloatComparisonEpsilonMult);
00173 }
00174 
00175 template <>
00176 UAVCAN_EXPORT
00177 inline bool areClose<double, double>(const double& left, const double& right)
00178 {
00179     return areFloatsClose(left, right, NumericTraits<double>::epsilon(),
00180                           NumericTraits<double>::epsilon() * FloatComparisonEpsilonMult);
00181 }
00182 
00183 template <>
00184 UAVCAN_EXPORT
00185 inline bool areClose<long double, long double>(const long double& left, const long double& right)
00186 {
00187     return areFloatsClose(left, right, NumericTraits<long double>::epsilon(),
00188                           NumericTraits<long double>::epsilon() * FloatComparisonEpsilonMult);
00189 }
00190 
00191 /*
00192  * Mixed floating type comparison - coercing larger type to smaller type
00193  */
00194 template <>
00195 UAVCAN_EXPORT
00196 inline bool areClose<float, double>(const float& left, const double& right)
00197 {
00198     return areClose(left, static_cast<float>(right));
00199 }
00200 
00201 template <>
00202 UAVCAN_EXPORT
00203 inline bool areClose<double, float>(const double& left, const float& right)
00204 {
00205     return areClose(static_cast<float>(left), right);
00206 }
00207 
00208 template <>
00209 UAVCAN_EXPORT
00210 inline bool areClose<float, long double>(const float& left, const long double& right)
00211 {
00212     return areClose(left, static_cast<float>(right));
00213 }
00214 
00215 template <>
00216 UAVCAN_EXPORT
00217 inline bool areClose<long double, float>(const long double& left, const float& right)
00218 {
00219     return areClose(static_cast<float>(left), right);
00220 }
00221 
00222 template <>
00223 UAVCAN_EXPORT
00224 inline bool areClose<double, long double>(const double& left, const long double& right)
00225 {
00226     return areClose(left, static_cast<double>(right));
00227 }
00228 
00229 template <>
00230 UAVCAN_EXPORT
00231 inline bool areClose<long double, double>(const long double& left, const double& right)
00232 {
00233     return areClose(static_cast<double>(left), right);
00234 }
00235 
00236 /**
00237  * Comparison against zero.
00238  * Helps to compare a floating point number against zero if the exact type is unknown.
00239  * For non-floating point types performs exact comparison against integer zero.
00240  */
00241 template <typename T>
00242 UAVCAN_EXPORT
00243 inline bool isCloseToZero(const T& x)
00244 {
00245     return x == 0;
00246 }
00247 
00248 template <>
00249 UAVCAN_EXPORT
00250 inline bool isCloseToZero<float>(const float& x)
00251 {
00252     return areClose(x, static_cast<float>(0.0F));
00253 }
00254 
00255 template <>
00256 UAVCAN_EXPORT
00257 inline bool isCloseToZero<double>(const double& x)
00258 {
00259     return areClose(x, static_cast<double>(0.0));
00260 }
00261 
00262 template <>
00263 UAVCAN_EXPORT
00264 inline bool isCloseToZero<long double>(const long double& x)
00265 {
00266     return areClose(x, static_cast<long double>(0.0L));
00267 }
00268 
00269 }
00270 
00271 #endif // UAVCAN_UTIL_COMPARISON_HPP_INCLUDED