diff --git a/cmake/etc/uncrustify b/cmake/etc/uncrustify index 45284f9..2c51ecc 160000 --- a/cmake/etc/uncrustify +++ b/cmake/etc/uncrustify @@ -1 +1 @@ -Subproject commit 45284f97623b7ae952020771846ccfe2e0a3cf58 +Subproject commit 2c51ecc3dc681b11a6283d8ab64e6410f8b11678 diff --git a/src/myx/math/almost_equal_relative.cpp b/src/myx/math/almost_equal_relative.cpp new file mode 100644 index 0000000..824c337 --- /dev/null +++ b/src/myx/math/almost_equal_relative.cpp @@ -0,0 +1,42 @@ +#include + +#include + +namespace myx { + +namespace math { + +bool almost_equal_relative( const float a, const float b, + const float maxRelDiff ) +{ + float diff = fabsf( a - b ); + float aN = fabsf( a ); + float bN = fabsf( b ); + float largest = ( bN > aN ) ? bN : aN; + + if ( diff <= largest * maxRelDiff ) + { + return( true ); + } + return( false ); +} + + +bool almost_equal_relative( const double a, const double b, + const double maxRelDiff ) +{ + double diff = fabs( a - b ); + double aN = fabs( a ); + double bN = fabs( b ); + double largest = ( bN > aN ) ? bN : aN; + + if ( diff <= largest * maxRelDiff ) + { + return( true ); + } + return( false ); +} + +} // namespace math + +} // namespace myx diff --git a/src/myx/math/almost_equal_relative.hpp b/src/myx/math/almost_equal_relative.hpp new file mode 100644 index 0000000..e4b44f2 --- /dev/null +++ b/src/myx/math/almost_equal_relative.hpp @@ -0,0 +1,31 @@ +#ifndef MYX_MATH_ALMOST_EQUAL_RELATIVE_HPP_ +#define MYX_MATH_ALMOST_EQUAL_RELATIVE_HPP_ + +#include + +// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + +namespace myx { + +namespace math { + +/** + * @brief Проверка на равенство двух аргументов типа float. + * Параметр maxRelDiff определяет максимальную относительную допустимую разницу, + * при которой аргументы считаются равными. + */ +bool almost_equal_relative( const float a, const float b, + const float maxRelDiff = FLT_EPSILON ); +/** + * @brief Проверка на равенство двух аргументов типа double. + * Параметр maxRelDiff определяет максимальную относительную допустимую разницу, + * при которой аргументы считаются равными. + */ +bool almost_equal_relative( const double a, const double b, + const double maxRelDiff = DBL_EPSILON ); + +} // namespace math + +} // namespace myx + +#endif // MYX_MATH_ALMOST_EQUAL_RELATIVE_HPP_ diff --git a/src/myx/math/almost_equal_relative_and_abs.cpp b/src/myx/math/almost_equal_relative_and_abs.cpp new file mode 100644 index 0000000..e3b0401 --- /dev/null +++ b/src/myx/math/almost_equal_relative_and_abs.cpp @@ -0,0 +1,56 @@ +#include + +#include + +namespace myx { + +namespace math { + +bool almost_equal_relative_and_abs( const float a, const float b, + const float maxAbsDiff, const float maxRelDiff ) +{ + // Check if the numbers are really close -- needed + // when comparing numbers near zero. + float diff = fabsf( a - b ); + if ( diff <= maxAbsDiff ) + { + return( true ); + } + + float aN = fabsf( a ); + float bN = fabsf( b ); + float largest = ( bN > aN ) ? bN : aN; + + if ( diff <= largest * maxRelDiff ) + { + return( true ); + } + return( false ); +} + + +bool almost_equal_relative_and_abs( const double a, const double b, + const double maxAbsDiff, const double maxRelDiff ) +{ + // Check if the numbers are really close -- needed + // when comparing numbers near zero. + double diff = fabs( a - b ); + if ( diff <= maxAbsDiff ) + { + return( true ); + } + + double aN = fabs( a ); + double bN = fabs( b ); + double largest = ( bN > aN ) ? bN : aN; + + if ( diff <= largest * maxRelDiff ) + { + return( true ); + } + return( false ); +} + +} // namespace math + +} // namespace myx diff --git a/src/myx/math/almost_equal_relative_and_abs.hpp b/src/myx/math/almost_equal_relative_and_abs.hpp new file mode 100644 index 0000000..0000e81 --- /dev/null +++ b/src/myx/math/almost_equal_relative_and_abs.hpp @@ -0,0 +1,31 @@ +#ifndef MYX_MATH_ALMOST_EQUAL_RELATIVE_AND_ABS_HPP_ +#define MYX_MATH_ALMOST_EQUAL_RELATIVE_AND_ABS_HPP_ + +#include + +namespace myx { + +namespace math { + +/** + * @brief Проверка на равенство двух аргументов типа float. + * Параметр maxAbsDiff определяет максимальную абсолютную допустимую разницу. + * Параметр maxRelDiff определяет максимальную относительную допустимую разницу, + * при которой аргументы считаются равными. + */ +bool almost_equal_relative_and_abs( const float a, const float b, + const float maxAbsDiff, const float maxRelDiff = FLT_EPSILON ); +/** + * @brief Проверка на равенство двух аргументов типа double. + * Параметр maxAbsDiff определяет максимальную абсолютную допустимую разницу. + * Параметр maxRelDiff определяет максимальную относительную допустимую разницу, + * при которой аргументы считаются равными. + */ +bool almost_equal_relative_and_abs( const double a, const double b, + const double maxAbsDiff, const double maxRelDiff = DBL_EPSILON ); + +} // namespace math + +} // namespace myx + +#endif // MYX_MATH_ALMOST_EQUAL_RELATIVE_AND_ABS_HPP_ diff --git a/src/myx/math/almost_equal_ulps.cpp b/src/myx/math/almost_equal_ulps.cpp new file mode 100644 index 0000000..3149dba --- /dev/null +++ b/src/myx/math/almost_equal_ulps.cpp @@ -0,0 +1,79 @@ +#include +#include + +#include + +namespace myx { + +namespace math { + +bool almost_equal_ulps( const float a, const float b, + const int maxUlpsDiff ) +{ + float_cmp_t uA( a ); + float_cmp_t uB( b ); + + // Если знаки разные, то числа не равны. + if ( uA.negative() != uB.negative() ) + { + // Кроме случая, когда +0==-0 + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" + #endif + if ( a == b ) + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif + { + return( true ); + } + return( false ); + } + + // Разница в младших битах. + auto ulpsDiff = std::abs( uA.i - uB.i ); + if ( ulpsDiff <= maxUlpsDiff ) + { + return( true ); + } + return( false ); +} // almost_equal_ulps + + +bool almost_equal_ulps( const double a, const double b, + const int maxUlpsDiff ) +{ + double_cmp_t uA( a ); + double_cmp_t uB( b ); + + // Если знаки разные, то числа не равны. + if ( uA.negative() != uB.negative() ) + { + // Кроме случая, когда +0==-0 + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" + #endif + if ( a == b ) + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif + { + return( true ); + } + return( false ); + } + + // Разница в младших битах. + auto ulpsDiff = std::abs( uA.i - uB.i ); + if ( ulpsDiff <= maxUlpsDiff ) + { + return( true ); + } + return( false ); +} // almost_equal_ulps + +} // namespace math + +} // namespace myx diff --git a/src/myx/math/almost_equal_ulps.hpp b/src/myx/math/almost_equal_ulps.hpp new file mode 100644 index 0000000..a7c0e11 --- /dev/null +++ b/src/myx/math/almost_equal_ulps.hpp @@ -0,0 +1,31 @@ +#ifndef MYX_MATH_ALMOST_EQUAL_ULPS_HPP_ +#define MYX_MATH_ALMOST_EQUAL_ULPS_HPP_ + +#include + +#include + +namespace myx { + +namespace math { + +/** + * @brief Проверка на равенство двух аргументов типа float. + * Параметр maxUlpsDiff определяет максимальную разницу между аргументами, + * при которой они считаются равными. + */ +bool almost_equal_ulps( const float a, const float b, + const int maxUlpsDiff ); +/** + * @brief Проверка на равенство двух аргументов типа double. + * Параметр maxUlpsDiff определяет максимальную разницу между аргументами, + * при которой они считаются равными. + */ +bool almost_equal_ulps( const double a, const double b, + const int maxUlpsDiff ); + +} // namespace math + +} // namespace myx + +#endif // MYX_MATH_ALMOST_EQUAL_ULPS_HPP_ diff --git a/src/myx/math/almost_equal_ulps_and_abs.cpp b/src/myx/math/almost_equal_ulps_and_abs.cpp new file mode 100644 index 0000000..d1f3f7c --- /dev/null +++ b/src/myx/math/almost_equal_ulps_and_abs.cpp @@ -0,0 +1,73 @@ +#include +#include + +#include + +namespace myx { + +namespace math { + +bool almost_equal_ulps_and_abs( const float a, const float b, + const float maxAbsDiff, const int maxUlpsDiff ) +{ + // Check if the numbers are really close -- needed + // when comparing numbers near zero. + float absDiff = fabsf( a - b ); + if ( absDiff <= maxAbsDiff ) + { + return( true ); + } + + float_cmp_t uA( a ); + float_cmp_t uB( b ); + + // Different signs means they do not match. + if ( uA.negative() != uB.negative() ) + { + return( false ); + } + + // Find the difference in ULPs. + int ulpsDiff = std::abs( uA.i - uB.i ); + if ( ulpsDiff <= maxUlpsDiff ) + { + return( true ); + } + + return( false ); +} // almost_equal_ulps_and_abs + + +bool almost_equal_ulps_and_abs( const double a, const double b, + const double maxAbsDiff, const int maxUlpsDiff ) +{ + // Check if the numbers are really close -- needed + // when comparing numbers near zero. + double absDiff = fabs( a - b ); + if ( absDiff <= maxAbsDiff ) + { + return( true ); + } + + double_cmp_t uA( a ); + double_cmp_t uB( b ); + + // Different signs means they do not match. + if ( uA.negative() != uB.negative() ) + { + return( false ); + } + + // Find the difference in ULPs. + auto ulpsDiff = std::abs( uA.i - uB.i ); + if ( ulpsDiff <= maxUlpsDiff ) + { + return( true ); + } + + return( false ); +} // almost_equal_ulps_and_abs + +} // namespace math + +} // namespace myx diff --git a/src/myx/math/almost_equal_ulps_and_abs.hpp b/src/myx/math/almost_equal_ulps_and_abs.hpp new file mode 100644 index 0000000..083ee24 --- /dev/null +++ b/src/myx/math/almost_equal_ulps_and_abs.hpp @@ -0,0 +1,29 @@ +#ifndef MYX_MATH_ALMOST_EQUAL_ULPS_HPP_ +#define MYX_MATH_ALMOST_EQUAL_ULPS_HPP_ + +namespace myx { + +namespace math { + +/** + * @brief Проверка на равенство двух аргументов типа float. + * Параметр maxAbsDiff определяет максимальную абсолютную допустимую разницу. + * Параметр maxUlpsDiff определяет максимальную разницу между аргументами, + * при которой они считаются равными. + */ +bool almost_equal_ulps_and_abs( const float a, const float b, + const float maxAbsDiff, const int maxUlpsDiff ); +/** + * @brief Проверка на равенство двух аргументов типа double. + * Параметр maxAbsDiff определяет максимальную абсолютную допустимую разницу. + * Параметр maxUlpsDiff определяет максимальную разницу между аргументами, + * при которой они считаются равными. + */ +bool almost_equal_ulps_and_abs( const double a, const double b, + const double maxAbsDiff, const int maxUlpsDiff ); + +} // namespace math + +} // namespace myx + +#endif // MYX_MATH_ALMOST_EQUAL_ULPS_HPP_ diff --git a/src/myx/math/float_cmp_types.hpp b/src/myx/math/float_cmp_types.hpp new file mode 100644 index 0000000..a60f640 --- /dev/null +++ b/src/myx/math/float_cmp_types.hpp @@ -0,0 +1,54 @@ +#ifndef MYX_MATH_FLOAT_CMP_TYPES_HPP_ +#define MYX_MATH_FLOAT_CMP_TYPES_HPP_ + +#include + +/** + * @brief Объединение для получения знака аргумента типа float. + */ +union float_cmp_t +{ + int32_t i; + float f; + + /** + * @brief Инициализация. + */ + float_cmp_t( float num = 0.0f ) : f( num ) + { + } + + /** + * @brief Функция, возращающая true, если значение отрицательное + */ + bool negative() const + { + return( i < 0 ); + } +}; + +/** + * @brief Объединение для получения знака аргумента типа double. + */ +union double_cmp_t +{ + int64_t i; + double d; + + /** + * @brief Инициализация. + */ + double_cmp_t( double num = 0.0l ) : d( num ) + { + } + + /** + * @brief Функция, возращающая true, если значение отрицательное + */ + bool negative() const + { + return( i < 0 ); + } +}; + +#endif // MYX_MATH_FLOAT_CMP_TYPES_HPP_