Browse Source

Added Math::TypeTraits, implementing fuzzy comparison based on type.

Fuzzy comparison is implemented only on floating-point types with
type-specific epsilon, on integral types classic operator== is used.
TypeTraits class is now extension of Math::TypeTraits (where it makes
sense).
pull/279/head
Vladimír Vondruš 14 years ago
parent
commit
f585d39146
  1. 2
      src/Math/Test/CMakeLists.txt
  2. 60
      src/Math/Test/TypeTraitsTest.cpp
  3. 36
      src/Math/Test/TypeTraitsTest.h
  4. 110
      src/Math/TypeTraits.h
  5. 41
      src/TypeTraits.h

2
src/Math/Test/CMakeLists.txt

@ -1,3 +1,5 @@
corrade_add_test(TypeTraitsTest TypeTraitsTest.h TypeTraitsTest.cpp)
corrade_add_test(VectorTest VectorTest.h VectorTest.cpp)
target_link_libraries(VectorTest ${CORRADE_UTILITY_LIBRARY})
corrade_add_test(Vector2Test Vector2Test.h Vector2Test.cpp)

60
src/Math/Test/TypeTraitsTest.cpp

@ -0,0 +1,60 @@
/*
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz>
This file is part of Magnum.
Magnum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 3
only, as published by the Free Software Foundation.
Magnum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License version 3 for more details.
*/
#include "TypeTraitsTest.h"
#include <QtTest/QTest>
#include "TypeTraits.h"
QTEST_APPLESS_MAIN(Magnum::Math::Test::TypeTraitsTest)
namespace Magnum { namespace Math { namespace Test {
void TypeTraitsTest::equalsIntegral() {
_equalsIntegral<unsigned char>();
_equalsIntegral<char>();
_equalsIntegral<unsigned short>();
_equalsIntegral<short>();
_equalsIntegral<unsigned int>();
_equalsIntegral<int>();
_equalsIntegral<unsigned long int>();
_equalsIntegral<long int>();
_equalsIntegral<unsigned long long>();
_equalsIntegral<long long>();
}
void TypeTraitsTest::equalsFloatingPoint() {
_equalsFloatingPoint<float>();
_equalsFloatingPoint<double>();
}
template<class T> void TypeTraitsTest::_equalsIntegral() {
QVERIFY(TypeTraits<T>::equals(1, 1+TypeTraits<T>::epsilon()));
QVERIFY(!TypeTraits<T>::equals(1, 2));
}
template<class T> void TypeTraitsTest::_equalsFloatingPoint() {
QVERIFY(TypeTraits<T>::equals(1.0f+TypeTraits<T>::epsilon()/2, 1.0f));
QVERIFY(!TypeTraits<T>::equals(1.0f+TypeTraits<T>::epsilon()*2, 1.0f));
QEXPECT_FAIL(0, "Comparing to infinity is broken", Continue);
QVERIFY(TypeTraits<T>::equals(std::numeric_limits<T>::infinity(),
std::numeric_limits<T>::infinity()));
QVERIFY(!TypeTraits<T>::equals(std::numeric_limits<T>::quiet_NaN(),
std::numeric_limits<T>::quiet_NaN()));
}
}}}

36
src/Math/Test/TypeTraitsTest.h

@ -0,0 +1,36 @@
#ifndef Magnum_Math_Test_TypeTraitsTest_h
#define Magnum_Math_Test_TypeTraitsTest_h
/*
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz>
This file is part of Magnum.
Magnum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 3
only, as published by the Free Software Foundation.
Magnum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License version 3 for more details.
*/
#include <QtCore/QObject>
namespace Magnum { namespace Math { namespace Test {
class TypeTraitsTest: public QObject {
Q_OBJECT
private slots:
void equalsFloatingPoint();
void equalsIntegral();
private:
template<class T> void _equalsFloatingPoint();
template<class T> void _equalsIntegral();
};
}}}
#endif

110
src/Math/TypeTraits.h

@ -0,0 +1,110 @@
#ifndef Magnum_Math_TypeTraits_h
#define Magnum_Math_TypeTraits_h
/*
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz>
This file is part of Magnum.
Magnum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 3
only, as published by the Free Software Foundation.
Magnum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License version 3 for more details.
*/
/** @file
* @brief Class Magnum::Math::TypeTraits
*/
#include <cmath>
namespace Magnum { namespace Math {
/**
@brief Traits class for numeric types
Traits classes are usable for detecting type features at compile time without
the need for repeated code such as method overloading or template
specialization for given types.
This class and class methods are specialized only for types where it makes
sense, it has empty implementation for unknown types or types which don't
support given feature, thus forcing the compilation stop with an error.
*/
template<class T> struct TypeTraits {
#ifdef DOXYGEN_GENERATING_OUTPUT
/* Development note: the following values are implemented as inline
functions, not as static const variables, because the compiler will
inline the return values instead of referencing to static data and
unlike static const variables these functions can return floats. */
/**
* @brief Epsilon value for fuzzy compare
*
* Returns 0 for integer types and reasonably small value for
* floating-point types.
*/
constexpr inline static T epsilon();
/**
* @brief Fuzzy compare
*
* Uses equality for integer types and fuzzy compare for floating-point
* types (using @ref epsilon value).
*/
static bool equals(T a, T b);
#endif
};
/** @bug Infinity comparison! */
/**
* @todo Implement better fuzzy comparison algorithm, like at
* http://floating-point-gui.de/errors/comparison/ or
* http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
template<class T> struct _TypeTraitsIntegral {
constexpr inline static T epsilon() { return 0; }
inline constexpr static bool equals(T a, T b) {
return a == b;
}
};
template<> struct TypeTraits<unsigned char>: public _TypeTraitsIntegral<unsigned char> {};
template<> struct TypeTraits<char>: public _TypeTraitsIntegral<char> {};
template<> struct TypeTraits<unsigned short>: public _TypeTraitsIntegral<unsigned short> {};
template<> struct TypeTraits<short>: public _TypeTraitsIntegral<short> {};
template<> struct TypeTraits<unsigned int>: public _TypeTraitsIntegral<unsigned int> {};
template<> struct TypeTraits<int>: public _TypeTraitsIntegral<int> {};
/* long is 32 bits somewhere and 64 bits elsewhere, so it cannot be mapped to
any of them */
template<> struct TypeTraits<long unsigned int>: public _TypeTraitsIntegral<long unsigned int> {};
template<> struct TypeTraits<long int>: public _TypeTraitsIntegral<long int> {};
template<> struct TypeTraits<unsigned long long>: public _TypeTraitsIntegral<unsigned long long> {};
template<> struct TypeTraits<long long>: public _TypeTraitsIntegral<long long> {};
template<class T> struct _TypeTraitsFloatingPoint {
inline static bool equals(T a, T b) {
return std::abs(a - b) < TypeTraits<T>::epsilon();
}
};
template<> struct TypeTraits<float>: public _TypeTraitsFloatingPoint<float> {
constexpr inline static float epsilon() { return 1.0e-6f; }
};
template<> struct TypeTraits<double>: public _TypeTraitsFloatingPoint<double> {
constexpr inline static double epsilon() { return 1.0e-12; }
};
#endif
}}
#endif

41
src/TypeTraits.h

@ -26,16 +26,13 @@ namespace Magnum {
/**
@brief Traits class for plain OpenGL types
Traits classes are usable for detecting type features at compile time without
the need for repeated code such as method overloading or template
specialization for given types.
@copydetails Math::TypeTraits
This class and class methods are specialized only for types where it makes
sense, it has empty implementation for unknown types or types which don't
support given feature, thus forcing the compilation stop with an error.
Where it makes sense, this class extends Math::TypeTraits with OpenGL-specific
traits.
*/
template<class T> struct TypeTraits {
#ifdef DOXYGEN_GENERATING_OUTPUT
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> struct TypeTraits: public Math::TypeTraits<T> {
/**
* @brief Type which can be used for indices
*
@ -75,14 +72,17 @@ template<class T> struct TypeTraits {
* Returns 1 for plain OpenGL types like GLint, but e.g. 3 for Vector3.
*/
constexpr inline static size_t count();
#endif
};
#else
template<class T> struct TypeTraits {};
#endif
/** @todo Other texture types, referenced in glTexImage2D function manual */
/** @todo Using Vector3 for textures? */
#ifndef DOXYGEN_GENERATING_OUTPUT
template<> struct TypeTraits<GLubyte> {
static_assert(sizeof(GLubyte) == sizeof(unsigned char), "GLubyte is not the same as unsigned char");
template<> struct TypeTraits<GLubyte>: public Math::TypeTraits<unsigned char> {
typedef GLubyte IndexType;
typedef GLubyte TextureType;
@ -91,7 +91,8 @@ template<> struct TypeTraits<GLubyte> {
inline constexpr static size_t count() { return 1; }
};
template<> struct TypeTraits<GLbyte> {
static_assert(sizeof(GLbyte) == sizeof(char), "GLbyte is not the same as char");
template<> struct TypeTraits<GLbyte>: public Math::TypeTraits<char> {
/* Can not be used for indices */
typedef GLbyte TextureType;
@ -100,7 +101,8 @@ template<> struct TypeTraits<GLbyte> {
inline constexpr static size_t count() { return 1; }
};
template<> struct TypeTraits<GLushort> {
static_assert(sizeof(GLushort) == sizeof(unsigned short), "GLushort is not the same as unsigned short");
template<> struct TypeTraits<GLushort>: public Math::TypeTraits<unsigned short> {
typedef GLushort IndexType;
typedef GLushort TextureType;
@ -109,7 +111,8 @@ template<> struct TypeTraits<GLushort> {
inline constexpr static size_t count() { return 1; }
};
template<> struct TypeTraits<GLshort> {
static_assert(sizeof(GLshort) == sizeof(short), "GLshort is not the same as short");
template<> struct TypeTraits<GLshort>: public Math::TypeTraits<short> {
/* Can not be used for indices */
typedef GLshort TextureType;
@ -118,7 +121,8 @@ template<> struct TypeTraits<GLshort> {
inline constexpr static size_t count() { return 1; }
};
template<> struct TypeTraits<GLuint> {
static_assert(sizeof(GLuint) == sizeof(unsigned int), "GLuint is not the same as unsigned int");
template<> struct TypeTraits<GLuint>: public Math::TypeTraits<unsigned int> {
typedef GLuint IndexType;
typedef GLuint TextureType;
@ -127,7 +131,8 @@ template<> struct TypeTraits<GLuint> {
inline constexpr static size_t count() { return 1; }
};
template<> struct TypeTraits<GLint> {
static_assert(sizeof(GLint) == sizeof(unsigned int), "GLint is not the same as int");
template<> struct TypeTraits<GLint>: public Math::TypeTraits<int> {
/* Can not be used for indices */
typedef GLint TextureType;
@ -136,7 +141,8 @@ template<> struct TypeTraits<GLint> {
inline constexpr static size_t count() { return 1; }
};
template<> struct TypeTraits<GLfloat> {
static_assert(sizeof(GLfloat) == sizeof(float), "GLfloat is not the same as float");
template<> struct TypeTraits<GLfloat>: public Math::TypeTraits<float> {
/* Can not be used for indices */
typedef GLfloat TextureType;
@ -145,7 +151,8 @@ template<> struct TypeTraits<GLfloat> {
inline constexpr static size_t count() { return 1; }
};
template<> struct TypeTraits<GLdouble> {
static_assert(sizeof(GLdouble) == sizeof(double), "GLdouble is not the same as double");
template<> struct TypeTraits<GLdouble>: public Math::TypeTraits<double> {
/* Can not be used for indices */
/* Can not be used for textures */

Loading…
Cancel
Save