libuav original
Dependents: UAVCAN UAVCAN_Subscriber
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
Generated on Tue Jul 12 2022 17:17:30 by 1.7.2