Browse Source

Math: added TypeTraits::equalsZero().

pull/175/merge
Vladimír Vondruš 10 years ago
parent
commit
74c75998b0
  1. 89
      src/Magnum/Math/Test/TypeTraitsTest.cpp
  2. 35
      src/Magnum/Math/TypeTraits.h

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

@ -41,8 +41,37 @@ struct TypeTraitsTest: Corrade::TestSuite::Tester {
template<class T> void equalsFloatingPointLarge();
template<class T> void equalsFloatingPointInfinity();
template<class T> void equalsFloatingPointNaN();
template<class T> void equalsZeroIntegral();
template<class T> void equalsZeroFloatingPoint();
template<class T> void equalsZeroFloatingPointSmall();
template<class T> void equalsZeroFloatingPointLarge();
};
namespace {
enum: std::size_t { EqualsZeroDataCount = 3 };
struct {
const char* name;
Float a, aStep;
Double b, bStep;
long double c, cStep;
Float get(Float) const { return a; }
Float getStep(Float) const { return aStep; }
Double get(Double) const { return b; }
Double getStep(Double) const { return bStep; }
long double get(long double) const { return c; }
long double getStep(long double) const { return cStep; }
} EqualsZeroData[EqualsZeroDataCount] = {
{"", -3.141592653589793f, 5.0e-5f, -3.141592653589793, 5.0e-14, -3.141592653589793l, 5.0e-17l},
{"small", 1.0e-6f, 5.0e-6f, -1.0e-15, 5.0e-15, 1.0e-18l, 5.0e-18l},
{"large", 12345.0f, 0.2f, 12345678901234.0, 0.2, -12345678901234567.0l, 0.2l},
};
}
TypeTraitsTest::TypeTraitsTest() {
addTests<TypeTraitsTest>({
&TypeTraitsTest::name,
@ -76,7 +105,27 @@ TypeTraitsTest::TypeTraitsTest() {
&TypeTraitsTest::equalsFloatingPointInfinity<Float>,
&TypeTraitsTest::equalsFloatingPointInfinity<Double>,
&TypeTraitsTest::equalsFloatingPointNaN<Float>,
&TypeTraitsTest::equalsFloatingPointNaN<Double>});
&TypeTraitsTest::equalsFloatingPointNaN<Double>,
&TypeTraitsTest::equalsZeroIntegral<UnsignedByte>,
&TypeTraitsTest::equalsZeroIntegral<Byte>,
&TypeTraitsTest::equalsZeroIntegral<UnsignedShort>,
&TypeTraitsTest::equalsZeroIntegral<Short>,
&TypeTraitsTest::equalsZeroIntegral<UnsignedInt>,
&TypeTraitsTest::equalsZeroIntegral<Int>,
#ifndef CORRADE_TARGET_EMSCRIPTEN
&TypeTraitsTest::equalsZeroIntegral<UnsignedLong>,
&TypeTraitsTest::equalsZeroIntegral<Long>,
#endif
});
addInstancedTests<TypeTraitsTest>({
&TypeTraitsTest::equalsZeroFloatingPoint<Float>,
&TypeTraitsTest::equalsZeroFloatingPoint<Double>,
#ifndef CORRADE_TARGET_EMSCRIPTEN
&TypeTraitsTest::equalsZeroFloatingPoint<long double>
#endif
}, EqualsZeroDataCount);
}
void TypeTraitsTest::name() {
@ -127,6 +176,44 @@ template<class T> void TypeTraitsTest::equalsFloatingPointNaN() {
Constants<T>::nan()));
}
namespace {
/* Argh! Why there is no standard std::abs() for unsigned types? */
template<class T, class U = typename std::enable_if<std::is_unsigned<T>::value>::type> T abs(T value) {
return value;
}
template<class T, class U = T, class V = typename std::enable_if<!std::is_unsigned<T>::value>::type> T abs(T value) {
return std::abs(value);
}
}
template<class T> void TypeTraitsTest::equalsZeroIntegral() {
setTestCaseName(std::string{"equalsZeroIntegral<"} + TypeTraits<T>::name() + ">");
const T a(-123);
const T b(-123);
const T magnitude = std::max(abs(a), abs(b));
CORRADE_VERIFY(TypeTraits<T>::equals(a, b));
CORRADE_VERIFY(TypeTraits<T>::equalsZero(a - b, magnitude));
CORRADE_VERIFY(!TypeTraits<T>::equalsZero(a - b + TypeTraits<T>::epsilon(), magnitude));
}
template<class T> void TypeTraitsTest::equalsZeroFloatingPoint() {
setTestCaseName(std::string{"equalsZeroFloatingPoint<"} + TypeTraits<T>::name() + ">");
setTestCaseDescription(EqualsZeroData[testCaseInstanceId()].name);
const T a = EqualsZeroData[testCaseInstanceId()].get(T{});
const T b = EqualsZeroData[testCaseInstanceId()].get(T{});
const T step = EqualsZeroData[testCaseInstanceId()].getStep(T{});
const T magnitude = std::max(abs(a), abs(b));
CORRADE_VERIFY(TypeTraits<T>::equals(a + step/T(2.0), b));
CORRADE_VERIFY(TypeTraits<T>::equalsZero(a + step/T(2.0) - b, magnitude));
CORRADE_VERIFY(!TypeTraits<T>::equals(a - step*T(2.0), b));
CORRADE_VERIFY(!TypeTraits<T>::equalsZero(a - step*T(2.0) - b, magnitude));
}
}}}
CORRADE_TEST_MAIN(Magnum::Math::Test::TypeTraitsTest)

35
src/Magnum/Math/TypeTraits.h

@ -72,6 +72,10 @@ namespace Implementation {
constexpr static bool equals(T a, T b) {
return a == b;
}
constexpr static bool equalsZero(T a, T) {
return !a;
}
};
}
@ -123,6 +127,22 @@ template<class T> struct TypeTraits: Implementation::TypeTraitsDefault<T> {
* value), pure equality comparison everywhere else.
*/
static bool equals(T a, T b);
/**
* @brief Fuzzy compare to zero with magnitude
*
* Uses fuzzy compare for floating-point types (using @ref epsilon()
* value), pure equality comparison everywhere else. Use this function when
* comparing e.g. a calculated nearly-zero difference with zero, knowing
* the magnitude of original values so the epsilon can be properly scaled.
* In other words, the following lines are equivalent:
* @code
* Float a, b;
* Math::TypeTraits<Float>::equals(a, b);
* Math::TypeTraits<Float>::equalsZero(a - b, Math::max(Math::abs(a), Math::abs(b)));
* @endcode
*/
static bool equalsZero(T a, T magnitude);
#endif
};
@ -187,6 +207,7 @@ template<class T> struct TypeTraitsFloatingPoint: TypeTraitsName<T> {
TypeTraitsFloatingPoint() = delete;
static bool equals(T a, T b);
static bool equalsZero(T a, T epsilon);
};
/* Adapted from http://floating-point-gui.de/errors/comparison/ */
@ -207,6 +228,20 @@ template<class T> bool TypeTraitsFloatingPoint<T>::equals(const T a, const T b)
return difference/(absA + absB) < TypeTraits<T>::epsilon();
}
template<class T> bool TypeTraitsFloatingPoint<T>::equalsZero(const T a, const T magnitude) {
/* Shortcut for binary equality */
if(a == T(0.0)) return true;
const T absA = std::abs(a);
/* The value is extremely close to zero, relative error is meaningless */
if(absA < TypeTraits<T>::epsilon())
return absA < TypeTraits<T>::epsilon();
/* Relative error */
return absA*T(0.5)/magnitude < TypeTraits<T>::epsilon();
}
}
template<> struct TypeTraits<Float>: Implementation::TypeTraitsFloatingPoint<Float> {

Loading…
Cancel
Save