diff --git a/src/Math/Math.h b/src/Math/Math.h index 7b56d7f5a..a0217f88c 100644 --- a/src/Math/Math.h +++ b/src/Math/Math.h @@ -16,6 +16,8 @@ */ #include +#include +#include #include "magnumVisibility.h" @@ -85,6 +87,43 @@ template inline constexpr T pow(T base) { */ size_t MAGNUM_EXPORT log(size_t base, size_t number); +/** +@brief Normalize floating-point value + +Converts integral value from full range of given (signed/unsigned) integral +type to value in range @f$ [0, 1] @f$. + +@attention To ensure the integral type is correctly detected when using +literals, this function should be called with both template parameters +explicit, e.g.: +@code +// Even if this is char literal, integral type is `int`, thus a = 0.1f +float a = normalize('\127'); + +// b = 1.0f +float b = normalize('\127'); +@endcode +*/ +template inline constexpr typename std::enable_if::value && std::is_integral::value, FloatingPoint>::type normalize(Integral value) { + return (FloatingPoint(value)-FloatingPoint(std::numeric_limits::min()))/ + (FloatingPoint(std::numeric_limits::max()) - FloatingPoint(std::numeric_limits::min())); +} + +/** +@brief Denormalize floating-point value + +Converts floating-point value in range @f$ [0, 1] @f$ to full range of given +integral type. + +@note For best precision, `FloatingPoint` type should be always larger that +resulting `Integral` type (e.g. `double` to `int`, `long double` to `long long`). +*/ +template inline constexpr typename std::enable_if::value && std::is_integral::value, Integral>::type denormalize(FloatingPoint value) { + return std::numeric_limits::min() + + Integral(value*std::numeric_limits::max()) - + Integral(value*std::numeric_limits::min()); +} + /** * @brief Angle in degrees * diff --git a/src/Math/Test/MathTest.cpp b/src/Math/Test/MathTest.cpp index 46babe120..dc02165f5 100644 --- a/src/Math/Test/MathTest.cpp +++ b/src/Math/Test/MathTest.cpp @@ -17,12 +17,16 @@ #include "Math.h" +using namespace std; + CORRADE_TEST_MAIN(Magnum::Math::Test::MathTest) namespace Magnum { namespace Math { namespace Test { MathTest::MathTest() { addTests(&MathTest::degrad, + &MathTest::normalize, + &MathTest::denormalize, &MathTest::pow, &MathTest::log); } @@ -33,6 +37,52 @@ void MathTest::degrad() { CORRADE_COMPARE(rad(Constants::pi()/2), Constants::pi()/2); } +void MathTest::normalize() { + /* Range for signed and unsigned */ + CORRADE_COMPARE((Math::normalize(-128)), 0.0f); + CORRADE_COMPARE((Math::normalize(127)), 1.0f); + CORRADE_COMPARE((Math::normalize(0)), 0.0f); + CORRADE_COMPARE((Math::normalize(255)), 1.0f); + + /* Between */ + CORRADE_COMPARE((Math::normalize(16384)), 0.750011f); + CORRADE_COMPARE((Math::normalize(-16384)), 0.250004f); + + /* Test overflow for large types */ + CORRADE_COMPARE((Math::normalize(numeric_limits::min())), 0.0f); + CORRADE_COMPARE((Math::normalize(numeric_limits::max())), 1.0f); + CORRADE_COMPARE((Math::normalize(0)), 0.0f); + CORRADE_COMPARE((Math::normalize(numeric_limits::max())), 1.0f); + + CORRADE_COMPARE((Math::normalize(numeric_limits::min())), 0.0); + CORRADE_COMPARE((Math::normalize(numeric_limits::max())), 1.0); + CORRADE_COMPARE((Math::normalize(0)), 0.0); + CORRADE_COMPARE((Math::normalize(numeric_limits::max())), 1.0); +} + +void MathTest::denormalize() { + /* Range for signed and unsigned */ + CORRADE_COMPARE(Math::denormalize(0.0f), -128); + CORRADE_COMPARE(Math::denormalize(1.0f), 127); + CORRADE_COMPARE(Math::denormalize(0.0f), 0); + CORRADE_COMPARE(Math::denormalize(1.0f), 255); + + /* Between */ + CORRADE_COMPARE(Math::denormalize(0.33f), -11142); + CORRADE_COMPARE(Math::denormalize(0.66f), 10484); + + /* Test overflow for large types */ + CORRADE_COMPARE(Math::denormalize(0.0f), numeric_limits::min()); + CORRADE_COMPARE(Math::denormalize(0.0f), 0); + CORRADE_COMPARE(Math::denormalize(0.0), numeric_limits::min()); + CORRADE_COMPARE(Math::denormalize(0.0), 0); + + CORRADE_COMPARE(Math::denormalize(1.0), numeric_limits::max()); + CORRADE_COMPARE(Math::denormalize(1.0), numeric_limits::max()); + CORRADE_COMPARE((Math::denormalize(1.0)), numeric_limits::max()); + CORRADE_COMPARE((Math::denormalize(1.0)), numeric_limits::max()); +} + void MathTest::pow() { CORRADE_COMPARE(Math::pow<10>(2ul), 1024ul); CORRADE_COMPARE(Math::pow<0>(3ul), 1ul); diff --git a/src/Math/Test/MathTest.h b/src/Math/Test/MathTest.h index 548b01e3c..b6dcba0c8 100644 --- a/src/Math/Test/MathTest.h +++ b/src/Math/Test/MathTest.h @@ -24,6 +24,8 @@ class MathTest: public Corrade::TestSuite::Tester { MathTest(); void degrad(); + void normalize(); + void denormalize(); void pow(); void log(); };