mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-23 12:23:33 +08:00
areClose(), isClose()
This commit is contained in:
@@ -107,13 +107,15 @@ struct UAVCAN_EXPORT ${t.cpp_type_name}
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Comparison operators are based on @ref uavcan::areClose(),
|
|
||||||
* which allows to safely compare floating point values.
|
|
||||||
*/
|
|
||||||
bool operator==(ParameterType rhs) const;
|
bool operator==(ParameterType rhs) const;
|
||||||
bool operator!=(ParameterType rhs) const { return !operator==(rhs); }
|
bool operator!=(ParameterType rhs) const { return !operator==(rhs); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This comparison is based on @ref uavcan::areClose(), which ensures proper comparison of
|
||||||
|
* floating point fields at any depth.
|
||||||
|
*/
|
||||||
|
bool isClose(ParameterType rhs) const;
|
||||||
|
|
||||||
static int encode(ParameterType self, ::uavcan::ScalarCodec& codec,
|
static int encode(ParameterType self, ::uavcan::ScalarCodec& codec,
|
||||||
::uavcan::TailArrayOptimizationMode tao_mode = ::uavcan::TailArrayOptEnabled);
|
::uavcan::TailArrayOptimizationMode tao_mode = ::uavcan::TailArrayOptEnabled);
|
||||||
|
|
||||||
@@ -182,6 +184,20 @@ UAVCAN_PACKED_END
|
|||||||
|
|
||||||
template <int _tmpl>
|
template <int _tmpl>
|
||||||
bool ${scope_prefix}<_tmpl>::operator==(ParameterType rhs) const
|
bool ${scope_prefix}<_tmpl>::operator==(ParameterType rhs) const
|
||||||
|
{
|
||||||
|
% if fields:
|
||||||
|
return
|
||||||
|
% for idx,a in enumerate(fields):
|
||||||
|
${a.name} == rhs.${a.name}${' &&' if (idx + 1) < len(fields) else ';'}
|
||||||
|
% endfor
|
||||||
|
% else:
|
||||||
|
(void)rhs;
|
||||||
|
return true;
|
||||||
|
% endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int _tmpl>
|
||||||
|
bool ${scope_prefix}<_tmpl>::isClose(ParameterType rhs) const
|
||||||
{
|
{
|
||||||
% if fields:
|
% if fields:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -577,11 +577,36 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This operator accepts any container with size() and [].
|
* This operator accepts any container with size() and [].
|
||||||
* Members are compared via @ref areClose().
|
* Members must be comparable via operator ==.
|
||||||
*/
|
*/
|
||||||
template <typename R>
|
template <typename R>
|
||||||
typename EnableIf<sizeof(((const R*)(0U))->size()) && sizeof((*((const R*)(0U)))[0]), bool>::Type
|
typename EnableIf<sizeof(((const R*)(0U))->size()) && sizeof((*((const R*)(0U)))[0]), bool>::Type
|
||||||
operator==(const R& rhs) const
|
operator==(const R& rhs) const
|
||||||
|
{
|
||||||
|
if (size() != rhs.size())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (SizeType i = 0; i < size(); i++) // Bitset does not have iterators
|
||||||
|
{
|
||||||
|
if (!(Base::at(i) == rhs[i]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method compares two arrays using @ref areClose(), which ensures proper comparison of
|
||||||
|
* floating point values, or DSDL data structures which contain floating point fields at any depth.
|
||||||
|
* Please refer to the documentation of @ref areClose() to learn more about how it works and how to
|
||||||
|
* define custom fuzzy comparison behavior.
|
||||||
|
* Any container with size() and [] is acceptable.
|
||||||
|
*/
|
||||||
|
template <typename R>
|
||||||
|
typename EnableIf<sizeof(((const R*)(0U))->size()) && sizeof((*((const R*)(0U)))[0]), bool>::Type
|
||||||
|
isClose(const R& rhs) const
|
||||||
{
|
{
|
||||||
if (size() != rhs.size())
|
if (size() != rhs.size())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ inline bool areFloatsExactlyEqual(const T& left, const T& right)
|
|||||||
* This function performs fuzzy comparison of two floating point numbers.
|
* This function performs fuzzy comparison of two floating point numbers.
|
||||||
* Type of T can be either float, double or long double.
|
* Type of T can be either float, double or long double.
|
||||||
* For details refer to http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
* For details refer to http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
||||||
|
* See also: @ref UAVCAN_FLOAT_COMPARISON_EPSILON_MULT.
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
UAVCAN_EXPORT
|
UAVCAN_EXPORT
|
||||||
@@ -55,15 +56,102 @@ inline bool areFloatsClose(T a, T b, const T absolute_epsilon, const T relative_
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic comparison function.
|
* This namespace contains implementation details for areClose().
|
||||||
|
* Don't try this at home.
|
||||||
|
*/
|
||||||
|
namespace are_close_impl_
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Applicable { char foo[1]; };
|
||||||
|
struct NotApplicable { long long foo[16]; };
|
||||||
|
|
||||||
|
template <typename This, typename Rhs>
|
||||||
|
struct HasIsCloseMethod
|
||||||
|
{
|
||||||
|
template <typename U, typename R, bool (U::*)(const R&) const> struct ConstRef { };
|
||||||
|
template <typename U, typename R, bool (U::*)(R) const> struct ByValue { };
|
||||||
|
|
||||||
|
template <typename U, typename R> static Applicable test(ConstRef<U, R, &U::isClose>*);
|
||||||
|
template <typename U, typename R> static Applicable test(ByValue<U, R, &U::isClose>*);
|
||||||
|
|
||||||
|
template <typename U, typename R> static NotApplicable test(...);
|
||||||
|
|
||||||
|
enum { Result = sizeof(test<This, Rhs>(NULL)) };
|
||||||
|
};
|
||||||
|
|
||||||
|
/// First stage: bool L::isClose(R)
|
||||||
|
template <typename L, typename R>
|
||||||
|
UAVCAN_EXPORT
|
||||||
|
inline bool areCloseImplFirst(const L& left, const R& right, IntToType<sizeof(Applicable)>)
|
||||||
|
{
|
||||||
|
return left.isClose(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Second stage: bool R::isClose(L)
|
||||||
|
template <typename L, typename R>
|
||||||
|
UAVCAN_EXPORT
|
||||||
|
inline bool areCloseImplSecond(const L& left, const R& right, IntToType<sizeof(Applicable)>)
|
||||||
|
{
|
||||||
|
return right.isClose(left);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Second stage: L == R
|
||||||
|
template <typename L, typename R>
|
||||||
|
UAVCAN_EXPORT
|
||||||
|
inline bool areCloseImplSecond(const L& left, const R& right, IntToType<sizeof(NotApplicable)>)
|
||||||
|
{
|
||||||
|
return left == right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// First stage: select either L == R or bool R::isClose(L)
|
||||||
|
template <typename L, typename R>
|
||||||
|
UAVCAN_EXPORT
|
||||||
|
inline bool areCloseImplFirst(const L& left, const R& right, IntToType<sizeof(NotApplicable)>)
|
||||||
|
{
|
||||||
|
return are_close_impl_::areCloseImplSecond(left, right,
|
||||||
|
IntToType<are_close_impl_::HasIsCloseMethod<R, L>::Result>());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace are_close_impl_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic fuzzy comparison function.
|
||||||
|
*
|
||||||
* This function properly handles floating point comparison, including mixed floating point type comparison,
|
* This function properly handles floating point comparison, including mixed floating point type comparison,
|
||||||
* e.g. float with long double.
|
* e.g. float with long double.
|
||||||
|
*
|
||||||
|
* Two objects of types A and B will be fuzzy comparable if either method is defined:
|
||||||
|
* - bool A::isClose(const B&)
|
||||||
|
* - bool A::isClose(B)
|
||||||
|
* - bool B::isClose(const A&)
|
||||||
|
* - bool B::isClose(A)
|
||||||
|
* Alternatively, a custom specialization of this function can be defined.
|
||||||
|
*
|
||||||
|
* Note that all floating types and their combinations are fuzzy comparable by default:
|
||||||
|
* - float
|
||||||
|
* - double
|
||||||
|
* - long double
|
||||||
|
*
|
||||||
|
* If the arguments aren't fuzzy comparable, this function will resort to the plain comparison operator ==.
|
||||||
|
*
|
||||||
|
* See also: @ref UAVCAN_FLOAT_COMPARISON_EPSILON_MULT.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* areClose(1.0, 1.0F) --> true
|
||||||
|
* areClose(1.0, 1.0F + std::numeric_limits<float>::epsilon()) --> true
|
||||||
|
* areClose(1.0, 1.1) --> false
|
||||||
|
* areClose("123", std::string("123")) --> true (using std::string's operator ==)
|
||||||
|
* areClose(inf, inf) --> true
|
||||||
|
* areClose(inf, -inf) --> false
|
||||||
|
* areClose(nan, nan) --> false
|
||||||
|
* areClose(123, "123") --> compilation error: operator == is not defined
|
||||||
*/
|
*/
|
||||||
template <typename L, typename R>
|
template <typename L, typename R>
|
||||||
UAVCAN_EXPORT
|
UAVCAN_EXPORT
|
||||||
inline bool areClose(const L& left, const R& right)
|
inline bool areClose(const L& left, const R& right)
|
||||||
{
|
{
|
||||||
return left == right;
|
return are_close_impl_::areCloseImplFirst(left, right,
|
||||||
|
IntToType<are_close_impl_::HasIsCloseMethod<L, R>::Result>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if __GNUC__
|
||||||
|
# pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <uavcan/transport/transfer_buffer.hpp>
|
#include <uavcan/transport/transfer_buffer.hpp>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
@@ -85,10 +89,12 @@ TEST(Dsdl, CloseComparison)
|
|||||||
ASSERT_TRUE(first == second);
|
ASSERT_TRUE(first == second);
|
||||||
|
|
||||||
first.vector[1].vector[1] = std::numeric_limits<double>::epsilon();
|
first.vector[1].vector[1] = std::numeric_limits<double>::epsilon();
|
||||||
ASSERT_TRUE(first == second); // Still equals
|
ASSERT_TRUE(first.isClose(second)); // Still close
|
||||||
|
ASSERT_FALSE(first == second); // But not exactly
|
||||||
|
|
||||||
first.vector[1].vector[1] = std::numeric_limits<float>::epsilon();
|
first.vector[1].vector[1] = std::numeric_limits<float>::epsilon();
|
||||||
ASSERT_FALSE(first == second); // Nope
|
ASSERT_FALSE(first.isClose(second)); // Nope
|
||||||
|
ASSERT_FALSE(first == second); // Ditto
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if __GNUC__
|
||||||
|
# pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <uavcan/marshal/types.hpp>
|
#include <uavcan/marshal/types.hpp>
|
||||||
#include <uavcan/transport/transfer_buffer.hpp>
|
#include <uavcan/transport/transfer_buffer.hpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user