Browse Source

Math: make TypeTraits::epsilon() consistent with Corrade.

The precision stays the same, but the long double variant is now exposed
on Emscripten as well, following a similar change in Corrade.
Additionally, the alien-looking _EQUALITY_PRECISION macros are now
unused and deprecated. For some reason these weren't ever prefixed with
MAGNUM_, and the ability to override those is an extremely rare use case
that would break half of the assumptions everywhere, so better not allow
that at all.

The TypeTraits test is further extended to compare directly the epsilons
between Magnum and Corrade, in addition to verifying that TestSuite and
TypeTraits have the same comparison results.
pull/432/head
Vladimír Vondruš 6 years ago
parent
commit
f2f66d764f
  1. 7
      doc/changelog.dox
  2. 36
      src/Magnum/Math/Test/TypeTraitsTest.cpp
  3. 52
      src/Magnum/Math/TypeTraits.h

7
doc/changelog.dox

@ -549,6 +549,13 @@ See also:
deprecated on the 3D variant, use separate deprecated on the 3D variant, use separate
@ref Shaders::MeshVisualizer3D::setTransformationMatrix() and @ref Shaders::MeshVisualizer3D::setTransformationMatrix() and
@ref Shaders::MeshVisualizer3D::setProjectionMatrix() instead @ref Shaders::MeshVisualizer3D::setProjectionMatrix() instead
- The unprefixed and alien-looking @cpp FLOAT_EQUALITY_PRECISION @ce,
@cpp DOUBLE_EQUALITY_PRECISION @ce and @cpp LONG_DOUBLE_EQUALITY_PRECISION @ce
macros are no longer used by any code and thus deprecated in favor of using
@ref Math::TypeTraits::epsilon() instead, which is also consistent with how
This also means it's no longer
possible to override equality comparison epsilons at compile time, but that
was a rarely (if ever) used feature.
@subsection changelog-latest-compatibility Potential compatibility breakages, removed APIs @subsection changelog-latest-compatibility Potential compatibility breakages, removed APIs

36
src/Magnum/Math/Test/TypeTraitsTest.cpp

@ -48,6 +48,8 @@ struct TypeTraitsTest: Corrade::TestSuite::Tester {
void underlyingTypeOf(); void underlyingTypeOf();
template<class T> void epsilonConsistentWithCorrade();
template<class T> void equalsIntegral(); template<class T> void equalsIntegral();
void equalsHalf(); void equalsHalf();
template<class T> void equalsFloatingPoint0(); template<class T> void equalsFloatingPoint0();
@ -116,6 +118,10 @@ TypeTraitsTest::TypeTraitsTest() {
&TypeTraitsTest::underlyingTypeOf, &TypeTraitsTest::underlyingTypeOf,
&TypeTraitsTest::epsilonConsistentWithCorrade<Float>,
&TypeTraitsTest::epsilonConsistentWithCorrade<Double>,
&TypeTraitsTest::epsilonConsistentWithCorrade<long double>,
&TypeTraitsTest::equalsIntegral<UnsignedByte>, &TypeTraitsTest::equalsIntegral<UnsignedByte>,
&TypeTraitsTest::equalsIntegral<Byte>, &TypeTraitsTest::equalsIntegral<Byte>,
&TypeTraitsTest::equalsIntegral<UnsignedShort>, &TypeTraitsTest::equalsIntegral<UnsignedShort>,
@ -131,19 +137,13 @@ TypeTraitsTest::TypeTraitsTest() {
&TypeTraitsTest::equalsFloatingPoint0<Float>, &TypeTraitsTest::equalsFloatingPoint0<Float>,
&TypeTraitsTest::equalsFloatingPoint0<Double>, &TypeTraitsTest::equalsFloatingPoint0<Double>,
#ifndef CORRADE_TARGET_EMSCRIPTEN
&TypeTraitsTest::equalsFloatingPoint0<long double>, &TypeTraitsTest::equalsFloatingPoint0<long double>,
#endif
&TypeTraitsTest::equalsFloatingPoint1<Float>, &TypeTraitsTest::equalsFloatingPoint1<Float>,
&TypeTraitsTest::equalsFloatingPoint1<Double>, &TypeTraitsTest::equalsFloatingPoint1<Double>,
#ifndef CORRADE_TARGET_EMSCRIPTEN
&TypeTraitsTest::equalsFloatingPoint1<long double>, &TypeTraitsTest::equalsFloatingPoint1<long double>,
#endif
&TypeTraitsTest::equalsFloatingPointLarge<Float>, &TypeTraitsTest::equalsFloatingPointLarge<Float>,
&TypeTraitsTest::equalsFloatingPointLarge<Double>, &TypeTraitsTest::equalsFloatingPointLarge<Double>,
#ifndef CORRADE_TARGET_EMSCRIPTEN
&TypeTraitsTest::equalsFloatingPointLarge<long double>, &TypeTraitsTest::equalsFloatingPointLarge<long double>,
#endif
&TypeTraitsTest::equalsFloatingPointInfinity<Float>, &TypeTraitsTest::equalsFloatingPointInfinity<Float>,
&TypeTraitsTest::equalsFloatingPointInfinity<Double>, &TypeTraitsTest::equalsFloatingPointInfinity<Double>,
&TypeTraitsTest::equalsFloatingPointNaN<Float>, &TypeTraitsTest::equalsFloatingPointNaN<Float>,
@ -268,6 +268,14 @@ void TypeTraitsTest::underlyingTypeOf() {
CORRADE_VERIFY((std::is_same<UnderlyingTypeOf<Matrix4<Float>>, Float>::value)); CORRADE_VERIFY((std::is_same<UnderlyingTypeOf<Matrix4<Float>>, Float>::value));
} }
template<class T> void TypeTraitsTest::epsilonConsistentWithCorrade() {
setTestCaseTemplateName(TypeTraits<T>::name());
/* Using VERIFY because we *don't* want fuzzy comparison in this case. The
equals*() tests below do further checks against TestSuite. */
CORRADE_VERIFY(TypeTraits<T>::epsilon() == Corrade::Utility::Implementation::FloatPrecision<T>::epsilon());
}
template<class T> void TypeTraitsTest::equalsIntegral() { template<class T> void TypeTraitsTest::equalsIntegral() {
setTestCaseTemplateName(TypeTraits<T>::name()); setTestCaseTemplateName(TypeTraits<T>::name());
@ -289,7 +297,9 @@ template<class T> void TypeTraitsTest::equalsFloatingPoint0() {
CORRADE_VERIFY(TypeTraits<T>::equals(T(0)+TypeTraits<T>::epsilon()/T(2), T(0))); CORRADE_VERIFY(TypeTraits<T>::equals(T(0)+TypeTraits<T>::epsilon()/T(2), T(0)));
CORRADE_VERIFY(!TypeTraits<T>::equals(T(0)+TypeTraits<T>::epsilon()*T(2), T(0))); CORRADE_VERIFY(!TypeTraits<T>::equals(T(0)+TypeTraits<T>::epsilon()*T(2), T(0)));
/* Ensure we have the same behavior as TestSuite */ /* Ensure we have the same behavior as TestSuite. Done in addition to the
epsilonConsistentWithCorrade() test above, since that one alone might
give a false sense of security. */
CORRADE_COMPARE(Corrade::TestSuite::Implementation::FloatComparator<T>{}( CORRADE_COMPARE(Corrade::TestSuite::Implementation::FloatComparator<T>{}(
T(0)+TypeTraits<T>::epsilon()/T(2), T(0)), T(0)+TypeTraits<T>::epsilon()/T(2), T(0)),
Corrade::TestSuite::ComparisonStatusFlags{}); Corrade::TestSuite::ComparisonStatusFlags{});
@ -304,7 +314,9 @@ template<class T> void TypeTraitsTest::equalsFloatingPoint1() {
CORRADE_VERIFY(TypeTraits<T>::equals(T(1)+TypeTraits<T>::epsilon()/T(2), T(1))); CORRADE_VERIFY(TypeTraits<T>::equals(T(1)+TypeTraits<T>::epsilon()/T(2), T(1)));
CORRADE_VERIFY(!TypeTraits<T>::equals(T(1)+TypeTraits<T>::epsilon()*T(3), T(1))); CORRADE_VERIFY(!TypeTraits<T>::equals(T(1)+TypeTraits<T>::epsilon()*T(3), T(1)));
/* Ensure we have the same behavior as TestSuite */ /* Ensure we have the same behavior as TestSuite. Done in addition to the
epsilonConsistentWithCorrade() test above, since that one alone might
give a false sense of security. */
CORRADE_COMPARE(Corrade::TestSuite::Implementation::FloatComparator<T>{}( CORRADE_COMPARE(Corrade::TestSuite::Implementation::FloatComparator<T>{}(
T(1)+TypeTraits<T>::epsilon()/T(2), T(1)), T(1)+TypeTraits<T>::epsilon()/T(2), T(1)),
Corrade::TestSuite::ComparisonStatusFlags{}); Corrade::TestSuite::ComparisonStatusFlags{});
@ -319,7 +331,9 @@ template<class T> void TypeTraitsTest::equalsFloatingPointLarge() {
CORRADE_VERIFY(TypeTraits<T>::equals(T(25)+TypeTraits<T>::epsilon()*T(2), T(25))); CORRADE_VERIFY(TypeTraits<T>::equals(T(25)+TypeTraits<T>::epsilon()*T(2), T(25)));
CORRADE_VERIFY(!TypeTraits<T>::equals(T(25)+TypeTraits<T>::epsilon()*T(75), T(25))); CORRADE_VERIFY(!TypeTraits<T>::equals(T(25)+TypeTraits<T>::epsilon()*T(75), T(25)));
/* Ensure we have the same behavior as TestSuite */ /* Ensure we have the same behavior as TestSuite. Done in addition to the
epsilonConsistentWithCorrade() test above, since that one alone might
give a false sense of security. */
CORRADE_COMPARE(Corrade::TestSuite::Implementation::FloatComparator<T>{}( CORRADE_COMPARE(Corrade::TestSuite::Implementation::FloatComparator<T>{}(
T(25)+TypeTraits<T>::epsilon()*T(2), T(25)), T(25)+TypeTraits<T>::epsilon()*T(2), T(25)),
Corrade::TestSuite::ComparisonStatusFlags{}); Corrade::TestSuite::ComparisonStatusFlags{});
@ -336,7 +350,9 @@ template<class T> void TypeTraitsTest::equalsFloatingPointInfinity() {
CORRADE_VERIFY(!TypeTraits<T>::equals(Constants<T>::inf(), CORRADE_VERIFY(!TypeTraits<T>::equals(Constants<T>::inf(),
-Constants<T>::inf())); -Constants<T>::inf()));
/* Ensure we have the same behavior as TestSuite */ /* Ensure we have the same behavior as TestSuite. Done in addition to the
epsilonConsistentWithCorrade() test above, since that one alone might
give a false sense of security. */
CORRADE_COMPARE(Corrade::TestSuite::Implementation::FloatComparator<T>{}( CORRADE_COMPARE(Corrade::TestSuite::Implementation::FloatComparator<T>{}(
Constants<T>::inf(), Constants<T>::inf()), Constants<T>::inf(), Constants<T>::inf()),
Corrade::TestSuite::ComparisonStatusFlags{}); Corrade::TestSuite::ComparisonStatusFlags{});

52
src/Magnum/Math/TypeTraits.h

@ -34,29 +34,42 @@
#include "Magnum/Math/Math.h" #include "Magnum/Math/Math.h"
#include "Magnum/Types.h" #include "Magnum/Types.h"
#ifdef MAGNUM_BUILD_DEPRECATED
#include <Corrade/Utility/Macros.h>
#endif
#ifdef MAGNUM_BUILD_DEPRECATED
/** /**
@brief Precision when testing floats for equality @brief Precision when testing floats for equality
@m_deprecated_since_latest Use @ref Magnum::Math::TypeTraits::epsilon()
instead.
They have "at least" 6 significant digits of precision, taking one digit less They have "at least" 6 significant digits of precision, taking one digit less
for more headroom. for more headroom.
*/ */
#ifndef FLOAT_EQUALITY_PRECISION #ifndef FLOAT_EQUALITY_PRECISION
#define FLOAT_EQUALITY_PRECISION 1.0e-5f #define FLOAT_EQUALITY_PRECISION \
CORRADE_DEPRECATED_MACRO(FLOAT_EQUALITY_PRECISION, "use Math::TypeTraits instead") 1.0e-5f
#endif #endif
/** /**
@brief Precision when testing doubles for equality @brief Precision when testing doubles for equality
@m_deprecated_since_latest Use @ref Magnum::Math::TypeTraits::epsilon()
instead.
They have "at least" 15 significant digits of precision, taking one digit less They have "at least" 15 significant digits of precision, taking one digit less
for more headroom. for more headroom.
*/ */
#ifndef DOUBLE_EQUALITY_PRECISION #ifndef DOUBLE_EQUALITY_PRECISION
#define DOUBLE_EQUALITY_PRECISION 1.0e-14 #define DOUBLE_EQUALITY_PRECISION \
CORRADE_DEPRECATED_MACRO(DOUBLE_EQUALITY_PRECISION, "use Math::TypeTraits instead") 1.0e-14
#endif #endif
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
/** /**
@brief Precision when testing long doubles for equality @brief Precision when testing long doubles for equality
@m_deprecated_since_latest Use @ref Magnum::Math::TypeTraits::epsilon()
instead.
They have "at least" 18 significant digits of precision, taking one digit less They have "at least" 18 significant digits of precision, taking one digit less
for more headroom. for more headroom.
@ -71,9 +84,12 @@ for more headroom.
*/ */
#ifndef LONG_DOUBLE_EQUALITY_PRECISION #ifndef LONG_DOUBLE_EQUALITY_PRECISION
#if !defined(_MSC_VER) && (!defined(CORRADE_TARGET_ANDROID) || __LP64__) #if !defined(_MSC_VER) && (!defined(CORRADE_TARGET_ANDROID) || __LP64__)
#define LONG_DOUBLE_EQUALITY_PRECISION 1.0e-17l #define LONG_DOUBLE_EQUALITY_PRECISION \
CORRADE_DEPRECATED_MACRO(LONG_DOUBLE_EQUALITY_PRECISION, "use Math::TypeTraits instead") 1.0e-17l
#else #else
#define LONG_DOUBLE_EQUALITY_PRECISION 1.0e-14 #define LONG_DOUBLE_EQUALITY_PRECISION \
CORRADE_DEPRECATED_MACRO(LONG_DOUBLE_EQUALITY_PRECISION, "use Math::TypeTraits instead") 1.0e-14l
#endif
#endif #endif
#endif #endif
#endif #endif
@ -338,8 +354,18 @@ template<class T> struct TypeTraits: Implementation::TypeTraitsDefault<T> {
* @brief Epsilon value for fuzzy compare * @brief Epsilon value for fuzzy compare
* *
* Returns minimal difference between numbers to be considered * Returns minimal difference between numbers to be considered
* inequal. Returns 1 for integer types and reasonably small value for * inequal. Not implemented for arbitrary types. Returns @cpp 1 @ce for
* floating-point types. Not implemented for arbitrary types. * integer types and
*
* - @cpp 1.0e-5f @ce for @cpp float @ce,
* - @cpp 1.0e-15 @ce for @cpp double @ce,
* - @cpp 1.0e-17l @ce for @cpp long double @ce on platforms where it is
* 80-bit, and @cpp 1.0e-14l @ce on platforms
* @ref CORRADE_LONG_DOUBLE_SAME_AS_DOUBLE "where it is 64-bit".
*
* This matches fuzzy comparison precision in @ref Corrade::TestSuite and
* is always one digit less than how @ref Corrade::Utility::Debug or
* @ref Corrade::Utility::format() prints given type.
*/ */
constexpr static T epsilon(); constexpr static T epsilon();
@ -414,9 +440,7 @@ namespace Implementation {
_c(Float) _c(Float)
_c(Half) _c(Half)
_c(Double) _c(Double)
#ifndef CORRADE_TARGET_EMSCRIPTEN
_c(long double) _c(long double)
#endif
#undef _c #undef _c
#endif #endif
@ -499,7 +523,7 @@ template<class T> bool TypeTraitsFloatingPoint<T>::equalsZero(const T a, const T
template<> struct TypeTraits<Float>: Implementation::TypeTraitsFloatingPoint<Float> { template<> struct TypeTraits<Float>: Implementation::TypeTraitsFloatingPoint<Float> {
typedef Float FloatingPointType; typedef Float FloatingPointType;
constexpr static Float epsilon() { return FLOAT_EQUALITY_PRECISION; } constexpr static Float epsilon() { return 1.0e-5f; }
}; };
/* A bit special -- using integer comparison for equality but presenting itself /* A bit special -- using integer comparison for equality but presenting itself
as a floating-point type so Color's fullChannel() works correctly */ as a floating-point type so Color's fullChannel() works correctly */
@ -509,15 +533,17 @@ template<> struct TypeTraits<Half>: Implementation::TypeTraitsName<Half>, Implem
template<> struct TypeTraits<Double>: Implementation::TypeTraitsFloatingPoint<Double> { template<> struct TypeTraits<Double>: Implementation::TypeTraitsFloatingPoint<Double> {
typedef Double FloatingPointType; typedef Double FloatingPointType;
constexpr static Double epsilon() { return DOUBLE_EQUALITY_PRECISION; } constexpr static Double epsilon() { return 1.0e-14; }
}; };
#ifndef CORRADE_TARGET_EMSCRIPTEN
template<> struct TypeTraits<long double>: Implementation::TypeTraitsFloatingPoint<long double> { template<> struct TypeTraits<long double>: Implementation::TypeTraitsFloatingPoint<long double> {
typedef long double FloatingPointType; typedef long double FloatingPointType;
constexpr static long double epsilon() { return LONG_DOUBLE_EQUALITY_PRECISION; } #ifndef CORRADE_LONG_DOUBLE_SAME_AS_DOUBLE
constexpr static long double epsilon() { return 1.0e-17l; }
#else
constexpr static long double epsilon() { return 1.0e-14l; }
#endif
}; };
#endif
namespace Implementation { namespace Implementation {

Loading…
Cancel
Save