diff --git a/Build/xcode5/PlayRho.xcodeproj/project.pbxproj b/Build/xcode5/PlayRho.xcodeproj/project.pbxproj index ed6127eee6..1340e987f0 100644 --- a/Build/xcode5/PlayRho.xcodeproj/project.pbxproj +++ b/Build/xcode5/PlayRho.xcodeproj/project.pbxproj @@ -38,7 +38,6 @@ 4726DD381D387D630012A882 /* Manifold.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4726DD351D387D630012A882 /* Manifold.hpp */; }; 472724231E310BB400C64921 /* Real.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 472724221E310BB400C64921 /* Real.cpp */; }; 472724251E310C7600C64921 /* float.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 472724241E310C7600C64921 /* float.cpp */; }; - 472724301E315E1A00C64921 /* Fixed.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4727242D1E315E1A00C64921 /* Fixed.hpp */; }; 472D35381F98C56200574B73 /* Intervals.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 472D35361F98C56200574B73 /* Intervals.hpp */; }; 472E24661F82C4EA0051E192 /* Interval.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 472E24651F82C4EA0051E192 /* Interval.cpp */; }; 472E301B1DBA925500DA821D /* VertexSet.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 472E30181DBA925500DA821D /* VertexSet.hpp */; }; @@ -161,7 +160,6 @@ 476FBF53253FB96E0057D5F3 /* Body.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 476FBF52253FB96E0057D5F3 /* Body.cpp */; }; 476FBF55254015260057D5F3 /* Contact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 476FBF54254015260057D5F3 /* Contact.cpp */; }; 4775A9381E05B032001C2332 /* StepConf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4775A9371E05B032001C2332 /* StepConf.cpp */; }; - 477D20061E32B6E80069D610 /* Fixed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 477D20051E32B6E80069D610 /* Fixed.cpp */; }; 477DB2AC1D1C4BE800846ED6 /* PositionSolverManifold.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 477DB2AA1D1C4BE800846ED6 /* PositionSolverManifold.cpp */; }; 477DB2AD1D1C4BE800846ED6 /* PositionSolverManifold.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 477DB2AB1D1C4BE800846ED6 /* PositionSolverManifold.hpp */; }; 4787D6BA1F2E946E008C115E /* PrismaticJointConf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4787D6B81F2E946E008C115E /* PrismaticJointConf.cpp */; }; @@ -322,8 +320,6 @@ 47D6D4C92665EB3900AF32B4 /* Compositor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 47D6D4C82665EB3900AF32B4 /* Compositor.cpp */; }; 47D6D59E2672B5CA00AF32B4 /* DistanceConf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 47D6D59C2672B5C900AF32B4 /* DistanceConf.cpp */; }; 47D6D59F2672B5CA00AF32B4 /* DistanceConf.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 47D6D59D2672B5CA00AF32B4 /* DistanceConf.hpp */; }; - 47E36F501FE8477000C10B79 /* FixedLimits.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 47E36F4E1FE8477000C10B79 /* FixedLimits.hpp */; }; - 47E36F541FE8534500C10B79 /* FixedMath.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 47E36F521FE8534500C10B79 /* FixedMath.hpp */; }; 47E70604255CDA4300A55BC1 /* ConstraintSolverConf.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 47E70603255CDA4300A55BC1 /* ConstraintSolverConf.hpp */; }; 47E7060B255CDBEE00A55BC1 /* ConstraintSolverConf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 47E7060A255CDBEE00A55BC1 /* ConstraintSolverConf.cpp */; }; 47E841701DB3FDC400E5F311 /* WorldManifold.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 47E8416F1DB3FDC400E5F311 /* WorldManifold.cpp */; }; @@ -536,7 +532,6 @@ 4726DD351D387D630012A882 /* Manifold.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Manifold.hpp; sourceTree = ""; }; 472724221E310BB400C64921 /* Real.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Real.cpp; sourceTree = ""; }; 472724241E310C7600C64921 /* float.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = float.cpp; sourceTree = ""; }; - 4727242D1E315E1A00C64921 /* Fixed.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Fixed.hpp; sourceTree = ""; }; 472D35331F968FB100574B73 /* UiState.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = UiState.hpp; sourceTree = ""; }; 472D35361F98C56200574B73 /* Intervals.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Intervals.hpp; sourceTree = ""; }; 472D353A1F99256700574B73 /* JointsTest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JointsTest.cpp; sourceTree = ""; }; @@ -671,7 +666,6 @@ 477329D61F9BEFA200C521B4 /* SolarSystem.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SolarSystem.cpp; sourceTree = ""; }; 4775A9371E05B032001C2332 /* StepConf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StepConf.cpp; sourceTree = ""; }; 47791F761F901B8700E257AF /* iforce2d_Trajectories.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = iforce2d_Trajectories.cpp; sourceTree = ""; }; - 477D20051E32B6E80069D610 /* Fixed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Fixed.cpp; sourceTree = ""; }; 477DB2AA1D1C4BE800846ED6 /* PositionSolverManifold.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PositionSolverManifold.cpp; sourceTree = ""; }; 477DB2AB1D1C4BE800846ED6 /* PositionSolverManifold.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PositionSolverManifold.hpp; sourceTree = ""; }; 4787D6B81F2E946E008C115E /* PrismaticJointConf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PrismaticJointConf.cpp; sourceTree = ""; }; @@ -772,8 +766,6 @@ 47D6D4C82665EB3900AF32B4 /* Compositor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Compositor.cpp; sourceTree = ""; }; 47D6D59C2672B5C900AF32B4 /* DistanceConf.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DistanceConf.cpp; sourceTree = ""; }; 47D6D59D2672B5CA00AF32B4 /* DistanceConf.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DistanceConf.hpp; sourceTree = ""; }; - 47E36F4E1FE8477000C10B79 /* FixedLimits.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FixedLimits.hpp; sourceTree = ""; }; - 47E36F521FE8534500C10B79 /* FixedMath.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FixedMath.hpp; sourceTree = ""; }; 47E70603255CDA4300A55BC1 /* ConstraintSolverConf.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ConstraintSolverConf.hpp; sourceTree = ""; }; 47E7060A255CDBEE00A55BC1 /* ConstraintSolverConf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConstraintSolverConf.cpp; sourceTree = ""; }; 47E8416F1DB3FDC400E5F311 /* WorldManifold.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WorldManifold.cpp; sourceTree = ""; }; @@ -1013,7 +1005,6 @@ 47D61F771F1FE3B700E702BD /* EdgeShape.cpp */, 474BC3FA1D41278800447DCD /* Epsilon.cpp */, 475931D11F26ABBD009BACA3 /* Filter.cpp */, - 477D20051E32B6E80069D610 /* Fixed.cpp */, 47B58F5F1F5B791500354C34 /* FlagGuard.cpp */, 472724241E310C7600C64921 /* float.cpp */, 474FDB521E72643D00F401AF /* forward_list.cpp */, @@ -1491,9 +1482,6 @@ 9901AEF72A9D1D130075122D /* detail */, 470F94B11EC4CB6C00AA3C82 /* Filter.hpp */, 4764085625047C090090F711 /* Finite.hpp */, - 4727242D1E315E1A00C64921 /* Fixed.hpp */, - 47E36F4E1FE8477000C10B79 /* FixedLimits.hpp */, - 47E36F521FE8534500C10B79 /* FixedMath.hpp */, 47B58F5C1F5B314400354C34 /* FlagGuard.hpp */, 80BB8943141C3E5900F1753A /* GrowableStack.hpp */, 47D28D9F1F72B9340094C032 /* Interval.hpp */, @@ -1628,7 +1616,6 @@ 472D35381F98C56200574B73 /* Intervals.hpp in Headers */, 4734B2211DC1340500F15E29 /* Span.hpp in Headers */, 80BB8998141C3E5900F1753A /* DiskShapeConf.hpp in Headers */, - 472724301E315E1A00C64921 /* Fixed.hpp in Headers */, 47C85D211F0DA14500F70C56 /* Templates.hpp in Headers */, 9901AF1E2AB248A80075122D /* NonPositiveChecker.hpp in Headers */, 80BB899A141C3E5900F1753A /* EdgeShapeConf.hpp in Headers */, @@ -1659,8 +1646,6 @@ 479132F226A52FD00011707B /* RayCastOpcode.hpp in Headers */, 9901AF0B2A9D8F7B0075122D /* WiderType.hpp in Headers */, 80BB89A4141C3E5900F1753A /* Math.hpp in Headers */, - 47E36F501FE8477000C10B79 /* FixedLimits.hpp in Headers */, - 47E36F541FE8534500C10B79 /* FixedMath.hpp in Headers */, 479DEA50267AABBE0070497D /* imgui.h in Headers */, 470F9B301EDF5165007EF7B6 /* Velocity.hpp in Headers */, 80BB89A6141C3E5900F1753A /* Settings.hpp in Headers */, @@ -1944,7 +1929,6 @@ 47D61F7A1F20229A00E702BD /* ChainShape.cpp in Sources */, 474B67221E80466D00B6D126 /* functional.cpp in Sources */, 476FBF4F253FAD690057D5F3 /* BodyConf.cpp in Sources */, - 477D20061E32B6E80069D610 /* Fixed.cpp in Sources */, 99CF863129C251CB00D9D112 /* ConstraintSolverConf.cpp in Sources */, 472E301E1DBA98BC00DA821D /* VertexSet.cpp in Sources */, 4731DE1E1DDD0B8700E7F931 /* DynamicTree.cpp in Sources */, diff --git a/Changes.md b/Changes.md index 396f8abe2c..c38a3df355 100644 --- a/Changes.md +++ b/Changes.md @@ -27,6 +27,9 @@ Here's an incomplete verbal summary of these changes: - Custom memory allocation via C++ allocators has been incorporated into the implementation of "hotter" code paths of the world stepping functionality. - More custom types are now defined in their own include and source files. - The `Testbed` application has been majorly updated to provide more support to the user for modifying examples at run-time. +- The `Fixed` class template and related code has been moved out from this project and into my separate [realnumb](https://github.com/louis-langholtz/realnumb) repository and project. +- CMake configuration now supports customization of some values in `Settings.hpp` via the following variables: `PLAYRHO_REAL_LINEARSLOP`, `PLAYRHO_REAL_MINVERTEXRADIUS`, `PLAYRHO_REAL_MAXVERTEXRADIUS`. +- CMake configuration for the `Real` alias defined in `Real.hpp` now supports inclusion of user specifiable header files for using a custom floating point type via the `PLAYRHO_REAL_INCLUDE` variable. Semicolons found within this variable are replaced with newlines. ## Box2D 2.3.2 To Release 1.1 diff --git a/Library/include/playrho/Fixed.hpp b/Library/include/playrho/Fixed.hpp deleted file mode 100644 index 99a75086b8..0000000000 --- a/Library/include/playrho/Fixed.hpp +++ /dev/null @@ -1,945 +0,0 @@ -/* - * Copyright (c) 2023 Louis Langholtz https://github.com/louis-langholtz/PlayRho - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef PLAYRHO_FIXED_HPP -#define PLAYRHO_FIXED_HPP - -/// @file -/// @brief Definition of the @c Fixed class and closely related code. - -#include -#include -#include -#include -#include - -#include -#include - -#include - -namespace playrho { -namespace detail { - -/// @brief Defaults for the Fixed class template. -/// @see Fixed. -template -struct FixedDefault {}; - -/// @brief Defaults for 32-bit integer instantiation of the Fixed class template. -template <> -struct FixedDefault -{ - /// @brief Fraction bits for the type this is specialized for. - static constexpr auto FractionBits = 9u; -}; - -/// @brief Defaults for 64-bit integer instantiation of the Fixed class template. -template <> -struct FixedDefault -{ - /// @brief Fraction bits for the type this is specialized for. - static constexpr auto FractionBits = 24u; -}; - -} - -/// @brief Template class for fixed-point numbers. -/// -/// @details This is a fixed point type template for a given base type using a given number -/// of fraction bits that satisfies the LiteralType named requirement. -/// -/// @see https://en.wikipedia.org/wiki/Fixed-point_arithmetic -/// @see https://en.cppreference.com/w/cpp/named_req/LiteralType -/// -template -class Fixed -{ -public: - - /// @brief Value type. - using value_type = BASE_TYPE; - - /// @brief Total number of bits. - static constexpr auto TotalBits = static_cast(sizeof(BASE_TYPE) * 8u); - - /// @brief Fraction bits. - static constexpr auto FractionBits = FRACTION_BITS; - - /// @brief Whole value bits. - static constexpr auto WholeBits = TotalBits - FractionBits; - - /// @brief Scale factor. - static constexpr auto ScaleFactor = static_cast(1u << FractionBits); - - /// @brief Compare result enumeration. - enum class CmpResult - { - Incomparable, - Equal, - LessThan, - GreaterThan - }; - - /// @brief Gets the min value this type is capable of expressing. - static constexpr Fixed GetMin() noexcept - { - return Fixed{1, scalar_type{1}}; - } - - /// @brief Gets an infinite value for this type. - static constexpr Fixed GetInfinity() noexcept - { - return Fixed{numeric_limits::max(), scalar_type{1}}; - } - - /// @brief Gets the max value this type is capable of expressing. - static constexpr Fixed GetMax() noexcept - { - // max reserved for +inf - return Fixed{numeric_limits::max() - 1, scalar_type{1}}; - } - - /// @brief Gets a NaN value for this type. - static constexpr Fixed GetNaN() noexcept - { - return Fixed{numeric_limits::lowest(), scalar_type{1}}; - } - - /// @brief Gets the negative infinity value for this type. - static constexpr Fixed GetNegativeInfinity() noexcept - { - // lowest reserved for NaN - return Fixed{numeric_limits::lowest() + 1, scalar_type{1}}; - } - - /// @brief Gets the lowest value this type is capable of expressing. - static constexpr Fixed GetLowest() noexcept - { - // lowest reserved for NaN - // lowest + 1 reserved for -inf - return Fixed{numeric_limits::lowest() + 2, scalar_type{1}}; - } - - /// @brief Gets the value from a floating point value. - template - static constexpr value_type GetFromFloat(T val) noexcept - { - static_assert(std::is_floating_point_v, "floating point value required"); - // Note: std::isnan(val) *NOT* constant expression, so can't use here! - return !(val <= 0 || val >= 0) // NOLINT(misc-redundant-expression) - ? GetNaN().m_value // force line-break - : (static_cast(val) > static_cast(GetMax())) // force line-break - ? GetInfinity().m_value // force line-break - : (static_cast(val) < static_cast(GetLowest())) // force line-break - ? GetNegativeInfinity().m_value // force line-break - : static_cast(val * ScaleFactor); - } - - /// @brief Gets the value from a signed integral value. - template - static constexpr value_type GetFromSignedInt(T val) noexcept - { - static_assert(std::is_integral_v, "integral value required"); - static_assert(std::is_signed_v, "must be signed"); - return (val > (GetMax().m_value / ScaleFactor))? GetInfinity().m_value: - (val < (GetLowest().m_value / ScaleFactor))? GetNegativeInfinity().m_value: - static_cast(val * ScaleFactor); - } - - /// @brief Gets the value from an unsigned integral value. - template - static constexpr value_type GetFromUnsignedInt(T val) noexcept - { - static_assert(std::is_integral_v, "integral value required"); - static_assert(!std::is_signed_v, "must be unsigned"); - const auto max = static_cast(GetMax().m_value / ScaleFactor); - return (val > max)? GetInfinity().m_value: static_cast(val) * ScaleFactor; - } - - Fixed() = default; - - /// @brief Initializing constructor. - constexpr Fixed(long double val) noexcept: - m_value{GetFromFloat(val)} - { - // Intentionally empty - } - - /// @brief Initializing constructor. - constexpr Fixed(double val) noexcept: - m_value{GetFromFloat(val)} - { - // Intentionally empty - } - - /// @brief Initializing constructor. - constexpr Fixed(float val) noexcept: - m_value{GetFromFloat(val)} - { - // Intentionally empty - } - - /// @brief Initializing constructor. - constexpr Fixed(unsigned long long val) noexcept: - m_value{GetFromUnsignedInt(val)} - { - // Intentionally empty. - } - - /// @brief Initializing constructor. - constexpr Fixed(unsigned long val) noexcept: - m_value{GetFromUnsignedInt(val)} - { - // Intentionally empty. - } - - /// @brief Initializing constructor. - constexpr Fixed(unsigned int val) noexcept: - m_value{GetFromUnsignedInt(val)} - { - // Intentionally empty. - } - - /// @brief Initializing constructor. - constexpr Fixed(long long val) noexcept: - m_value{GetFromSignedInt(val)} - { - // Intentionally empty. - } - - /// @brief Initializing constructor. - constexpr Fixed(long val) noexcept: - m_value{GetFromSignedInt(val)} - { - // Intentionally empty. - } - - /// @brief Initializing constructor. - constexpr Fixed(int val) noexcept: - m_value{GetFromSignedInt(val)} - { - // Intentionally empty. - } - - /// @brief Initializing constructor. - constexpr Fixed(short val) noexcept: - m_value{GetFromSignedInt(val)} - { - // Intentionally empty. - } - - /// @brief Initializing constructor. - constexpr Fixed(value_type val, unsigned int fraction) noexcept: - m_value{static_cast(static_cast(val * ScaleFactor) | fraction)} - { - // Intentionally empty. - } - - /// @brief Initializing constructor. - template - constexpr Fixed(const Fixed val) noexcept: - Fixed(static_cast(val)) - { - // Intentionally empty - } - - // Methods - - /// @brief Converts the value to the expressed type. - template - constexpr T ConvertTo() const noexcept - { - return isnan()? std::numeric_limits::signaling_NaN(): - !isfinite()? std::numeric_limits::infinity() * static_cast(getsign()): - static_cast(m_value) / static_cast(ScaleFactor); - } - - /// @brief Compares this value to the given one. - constexpr CmpResult Compare(const Fixed other) const noexcept - { - if (isnan() || other.isnan()) - { - return CmpResult::Incomparable; - } - if (m_value < other.m_value) - { - return CmpResult::LessThan; - } - if (m_value > other.m_value) - { - return CmpResult::GreaterThan; - } - return CmpResult::Equal; - } - - // Unary operations - - /// @brief Long double operator. - explicit constexpr operator long double() const noexcept - { - return ConvertTo(); - } - - /// @brief Double operator. - explicit constexpr operator double() const noexcept - { - return ConvertTo(); - } - - /// @brief Float operator. - explicit constexpr operator float() const noexcept - { - return ConvertTo(); - } - - /// @brief Long long operator. - explicit constexpr operator long long() const noexcept - { - return m_value / ScaleFactor; - } - - /// @brief Long operator. - explicit constexpr operator long() const noexcept - { - return m_value / ScaleFactor; - } - - /// @brief Unsigned long long operator. - explicit constexpr operator unsigned long long() const noexcept - { - return static_cast(m_value / ScaleFactor); - } - - /// @brief Unsigned long operator. - explicit constexpr operator unsigned long() const noexcept - { - return static_cast(m_value / ScaleFactor); - } - - /// @brief Unsigned int operator. - explicit constexpr operator unsigned int() const noexcept - { - return static_cast(m_value / ScaleFactor); - } - - /// @brief int operator. - explicit constexpr operator int() const noexcept - { - return static_cast(m_value / ScaleFactor); - } - - /// @brief short operator. - explicit constexpr operator short() const noexcept - { - return static_cast(m_value / ScaleFactor); - } - - /// @brief Negation operator. - constexpr Fixed operator- () const noexcept - { - return (isnan())? *this: Fixed{-m_value, scalar_type{1}}; - } - - /// @brief Positive operator. - constexpr Fixed operator+ () const noexcept - { - return *this; - } - - /// @brief Boolean operator. - explicit constexpr operator bool() const noexcept - { - return m_value != 0; - } - - /// @brief Logical not operator. - constexpr bool operator! () const noexcept - { - return m_value == 0; - } - - /// @brief Addition assignment operator. - constexpr Fixed& operator+= (Fixed val) noexcept - { - if (isnan() || val.isnan() - || ((m_value == GetInfinity().m_value) && (val.m_value == GetNegativeInfinity().m_value)) - || ((m_value == GetNegativeInfinity().m_value) && (val.m_value == GetInfinity().m_value)) - ) - { - *this = GetNaN(); - } - else if (val.m_value == GetInfinity().m_value) - { - m_value = GetInfinity().m_value; - } - else if (val.m_value == GetNegativeInfinity().m_value) - { - m_value = GetNegativeInfinity().m_value; - } - else if (isfinite() && val.isfinite()) - { - const auto result = wider_type{m_value} + val.m_value; - if (result > GetMax().m_value) - { - // overflow from max - m_value = GetInfinity().m_value; - } - else if (result < GetLowest().m_value) - { - // overflow from lowest - m_value = GetNegativeInfinity().m_value; - } - else - { - m_value = static_cast(result); - } - } - return *this; - } - - /// @brief Subtraction assignment operator. - constexpr Fixed& operator-= (Fixed val) noexcept - { - if (isnan() || val.isnan() - || ((m_value == GetInfinity().m_value) && (val.m_value == GetInfinity().m_value)) - || ((m_value == GetNegativeInfinity().m_value) && (val.m_value == GetNegativeInfinity().m_value)) - ) - { - *this = GetNaN(); - } - else if (val.m_value == GetInfinity().m_value) - { - m_value = GetNegativeInfinity().m_value; - } - else if (val.m_value == GetNegativeInfinity().m_value) - { - m_value = GetInfinity().m_value; - } - else if (isfinite() && val.isfinite()) - { - const auto result = wider_type{m_value} - val.m_value; - if (result > GetMax().m_value) - { - // overflow from max - m_value = GetInfinity().m_value; - } - else if (result < GetLowest().m_value) - { - // overflow from lowest - m_value = GetNegativeInfinity().m_value; - } - else - { - m_value = static_cast(result); - } - } - return *this; - } - - /// @brief Multiplication assignment operator. - constexpr Fixed& operator*= (Fixed val) noexcept - { - if (isnan() || val.isnan()) - { - *this = GetNaN(); - } - else if (!isfinite() || !val.isfinite()) - { - if (m_value == 0 || val.m_value == 0) - { - *this = GetNaN(); - } - else - { - *this = ((m_value > 0) != (val.m_value > 0))? -GetInfinity(): GetInfinity(); - } - } - else - { - const auto product = wider_type{m_value} * wider_type{val.m_value}; - const auto result = product / ScaleFactor; - - if (product != 0 && result == 0) - { - // underflow - m_value = static_cast(result); - } - else if (result > GetMax().m_value) - { - // overflow from max - m_value = GetInfinity().m_value; - } - else if (result < GetLowest().m_value) - { - // overflow from lowest - m_value = GetNegativeInfinity().m_value; - } - else - { - m_value = static_cast(result); - } - } - return *this; - } - - /// @brief Division assignment operator. - constexpr Fixed& operator/= (Fixed val) noexcept - { - if (isnan() || val.isnan()) - { - *this = GetNaN(); - } - else if (!isfinite() && !val.isfinite()) - { - *this = GetNaN(); - } - else if (!isfinite()) - { - *this = ((m_value > 0) != (val.m_value > 0))? -GetInfinity(): GetInfinity(); - } - else if (!val.isfinite()) - { - *this = 0; - } - else - { - const auto product = wider_type{m_value} * ScaleFactor; - const auto result = product / val.m_value; - - if (product != 0 && result == 0) - { - // underflow - m_value = static_cast(result); - } - else if (result > GetMax().m_value) - { - // overflow from max - m_value = GetInfinity().m_value; - } - else if (result < GetLowest().m_value) - { - // overflow from lowest - m_value = GetNegativeInfinity().m_value; - } - else - { - m_value = static_cast(result); - } - } - return *this; - } - - /// @brief Modulo operator. - constexpr Fixed& operator%= (Fixed val) noexcept - { - assert(!isnan()); - assert(!val.isnan()); - - m_value %= val.m_value; - return *this; - } - - /// @brief Is finite. - constexpr bool isfinite() const noexcept - { - return (m_value > GetNegativeInfinity().m_value) - && (m_value < GetInfinity().m_value); - } - - /// @brief Is NaN. - constexpr bool isnan() const noexcept - { - return m_value == GetNaN().m_value; - } - - /// @brief Gets this value's sign. - constexpr int getsign() const noexcept - { - return (m_value >= 0)? +1: -1; - } - -private: - - /// @brief Widened type alias. - using wider_type = typename detail::Wider::type; - - /// @brief Unsigned widened type alias. - using unsigned_wider_type = std::make_unsigned_t; - - /// @brief Scalar type. - struct scalar_type - { - value_type value = 1; ///< Value. - }; - - /// @brief Numeric limits type alias. - using numeric_limits = std::numeric_limits; - - /// @brief Initializing constructor. - constexpr Fixed(value_type val, scalar_type scalar) noexcept: - m_value{val * scalar.value} - { - // Intentionally empty. - } - - value_type m_value; ///< Value in internal form. -}; - -/// @brief Equality operator. -template -constexpr bool operator== (Fixed lhs, Fixed rhs) noexcept -{ - return lhs.Compare(rhs) == Fixed::CmpResult::Equal; -} - -/// @brief Inequality operator. -template -constexpr bool operator!= (Fixed lhs, Fixed rhs) noexcept -{ - return lhs.Compare(rhs) != Fixed::CmpResult::Equal; -} - -/// @brief Less-than operator. -template -constexpr bool operator< (Fixed lhs, Fixed rhs) noexcept -{ - return lhs.Compare(rhs) == Fixed::CmpResult::LessThan; -} - -/// @brief Greater-than operator. -template -constexpr bool operator> (Fixed lhs, Fixed rhs) noexcept -{ - return lhs.Compare(rhs) == Fixed::CmpResult::GreaterThan; -} - -/// @brief Less-than or equal-to operator. -template -constexpr bool operator<= (Fixed lhs, Fixed rhs) noexcept -{ - const auto result = lhs.Compare(rhs); - return result == Fixed::CmpResult::LessThan || - result == Fixed::CmpResult::Equal; -} - -/// @brief Greater-than or equal-to operator. -template -constexpr bool operator>= (Fixed lhs, Fixed rhs) noexcept -{ - const auto result = lhs.Compare(rhs); - return result == Fixed::CmpResult::GreaterThan || result == Fixed::CmpResult::Equal; -} - -/// @brief Addition operator. -template -constexpr Fixed operator+ (Fixed lhs, Fixed rhs) noexcept -{ - lhs += rhs; - return lhs; -} - -/// @brief Subtraction operator. -template -constexpr Fixed operator- (Fixed lhs, Fixed rhs) noexcept -{ - lhs -= rhs; - return lhs; -} - -/// @brief Multiplication operator. -template -constexpr Fixed operator* (Fixed lhs, Fixed rhs) noexcept -{ - lhs *= rhs; - return lhs; -} - -/// @brief Division operator. -template -constexpr Fixed operator/ (Fixed lhs, Fixed rhs) noexcept -{ - lhs /= rhs; - return lhs; -} - -/// @brief Modulo operator. -template -constexpr Fixed operator% (Fixed lhs, Fixed rhs) noexcept -{ - lhs %= rhs; - return lhs; -} - -/// @brief Gets whether a given value is almost zero. -/// @details An almost zero value is "subnormal". Dividing by these values can lead to -/// odd results like a divide by zero trap occurring. -/// @return true if the given value is almost zero, false otherwise. -template -constexpr bool AlmostZero(Fixed value) -{ - return value == 0; -} - -/// @brief Determines whether the given two values are "almost equal". -template -constexpr bool AlmostEqual(Fixed x, Fixed y, int ulp = 2) -{ - return abs(x - y) <= Fixed{0, static_cast(ulp)}; -} - -#ifdef CONFLICT_WITH_GETINVALID -/// @brief Gets an invalid value. -template -constexpr Fixed GetInvalid() noexcept -{ - return Fixed::GetNaN(); -} -#endif // CONFLICT_WITH_GETINVALID - -/// @brief Output stream operator. -template -inline ::std::ostream& operator<<(::std::ostream& os, const Fixed& value) -{ - return os << static_cast(value); -} - -/// @brief 32-bit fixed precision type. -/// @details This is a 32-bit fixed precision type with a Q number-format of -/// Q23.9. -/// -/// @warning The available numeric fidelity of any 32-bit fixed point type is very limited. -/// Using a 32-bit fixed point type for Real should only be considered for simulations -/// where it's been found to work and where the dynamics won't be changing between runs. -/// -/// @note Maximum value (with 9 fraction bits) is approximately 4194303.99609375. -/// @note Minimum value (with 9 fraction bits) is approximately 0.001953125. -/// -/// @see Fixed, Real -/// @see https://en.wikipedia.org/wiki/Q_(number_format) -/// -using Fixed32 = Fixed::FractionBits>; - -// Fixed32 free functions. - -/// @brief Gets an invalid value. -template <> -constexpr Fixed32 GetInvalid() noexcept -{ - return Fixed32::GetNaN(); -} - -/// @brief Addition operator. -constexpr Fixed32 operator+ (Fixed32 lhs, Fixed32 rhs) noexcept -{ - lhs += rhs; - return lhs; -} - -/// @brief Subtraction operator. -constexpr Fixed32 operator- (Fixed32 lhs, Fixed32 rhs) noexcept -{ - lhs -= rhs; - return lhs; -} - -/// @brief Multiplication operator. -constexpr Fixed32 operator* (Fixed32 lhs, Fixed32 rhs) noexcept -{ - lhs *= rhs; - return lhs; -} - -/// @brief Division operator. -constexpr Fixed32 operator/ (Fixed32 lhs, Fixed32 rhs) noexcept -{ - lhs /= rhs; - return lhs; -} - -/// @brief Modulo operator. -constexpr Fixed32 operator% (Fixed32 lhs, Fixed32 rhs) noexcept -{ - lhs %= rhs; - return lhs; -} - -/// @brief Equality operator. -constexpr bool operator== (Fixed32 lhs, Fixed32 rhs) noexcept -{ - return lhs.Compare(rhs) == Fixed32::CmpResult::Equal; -} - -/// @brief Inequality operator. -constexpr bool operator!= (Fixed32 lhs, Fixed32 rhs) noexcept -{ - return lhs.Compare(rhs) != Fixed32::CmpResult::Equal; -} - -/// @brief Less-than or equal-to operator. -constexpr bool operator <= (Fixed32 lhs, Fixed32 rhs) noexcept -{ - const auto result = lhs.Compare(rhs); - return (result == Fixed32::CmpResult::LessThan) || (result == Fixed32::CmpResult::Equal); -} - -/// @brief Greater-than or equal-to operator. -constexpr bool operator >= (Fixed32 lhs, Fixed32 rhs) noexcept -{ - const auto result = lhs.Compare(rhs); - return (result == Fixed32::CmpResult::GreaterThan) || (result == Fixed32::CmpResult::Equal); -} - -/// @brief Less-than operator. -constexpr bool operator < (Fixed32 lhs, Fixed32 rhs) noexcept -{ - const auto result = lhs.Compare(rhs); - return result == Fixed32::CmpResult::LessThan; -} - -/// @brief Greater-than operator. -constexpr bool operator > (Fixed32 lhs, Fixed32 rhs) noexcept -{ - const auto result = lhs.Compare(rhs); - return result == Fixed32::CmpResult::GreaterThan; -} - -#ifdef PLAYRHO_INT128 -// Fixed64 free functions. - -/// @brief 64-bit fixed precision type. -/// @details This is a 64-bit fixed precision type with a Q number-format of -/// Q40.24. -/// -/// @note Minimum value (with 24 fraction bits) is approximately -/// 5.9604644775390625e-08. -/// @note Maximum value (with 24 fraction bits) is approximately -/// 549755813888. -/// -/// @see Fixed, Real -/// @see https://en.wikipedia.org/wiki/Q_(number_format) -/// -using Fixed64 = Fixed::FractionBits>; - -/// @brief Gets an invalid value. -template <> -constexpr Fixed64 GetInvalid() noexcept -{ - return Fixed64::GetNaN(); -} - -/// @brief Addition operator. -constexpr Fixed64 operator+ (Fixed64 lhs, Fixed64 rhs) noexcept -{ - lhs += rhs; - return lhs; -} - -/// @brief Subtraction operator. -constexpr Fixed64 operator- (Fixed64 lhs, Fixed64 rhs) noexcept -{ - lhs -= rhs; - return lhs; -} - -/// @brief Multiplication operator. -constexpr Fixed64 operator* (Fixed64 lhs, Fixed64 rhs) noexcept -{ - lhs *= rhs; - return lhs; -} - -/// @brief Division operator. -constexpr Fixed64 operator/ (Fixed64 lhs, Fixed64 rhs) noexcept -{ - lhs /= rhs; - return lhs; -} - -constexpr Fixed64 operator% (Fixed64 lhs, Fixed64 rhs) noexcept -{ - lhs %= rhs; - return lhs; -} - -/// @brief Equality operator. -constexpr bool operator== (Fixed64 lhs, Fixed64 rhs) noexcept -{ - return lhs.Compare(rhs) == Fixed64::CmpResult::Equal; -} - -/// @brief Inequality operator. -constexpr bool operator!= (Fixed64 lhs, Fixed64 rhs) noexcept -{ - return lhs.Compare(rhs) != Fixed64::CmpResult::Equal; -} - -constexpr bool operator <= (Fixed64 lhs, Fixed64 rhs) noexcept -{ - const auto result = lhs.Compare(rhs); - return (result == Fixed64::CmpResult::LessThan) || (result == Fixed64::CmpResult::Equal); -} - -constexpr bool operator >= (Fixed64 lhs, Fixed64 rhs) noexcept -{ - const auto result = lhs.Compare(rhs); - return (result == Fixed64::CmpResult::GreaterThan) || (result == Fixed64::CmpResult::Equal); -} - -constexpr bool operator < (Fixed64 lhs, Fixed64 rhs) noexcept -{ - const auto result = lhs.Compare(rhs); - return result == Fixed64::CmpResult::LessThan; -} - -constexpr bool operator > (Fixed64 lhs, Fixed64 rhs) noexcept -{ - const auto result = lhs.Compare(rhs); - return result == Fixed64::CmpResult::GreaterThan; -} - -/// @brief Specialization of the Wider trait for the Fixed32 type. -template<> struct detail::Wider { - using type = Fixed64; ///< Wider type. -}; - -#endif /* PLAYRHO_INT128 */ - -} // namespace playrho - -/// @brief Type info specialization for playrho::Fixed32. -template <> -struct playrho::detail::TypeInfo -{ - /// @brief The specialized name for the playrho::Fixed32 type. - static constexpr const char* name = "Fixed32"; -}; - -#ifdef PLAYRHO_INT128 -/// @brief Type info specialization for playrho::Fixed64. -template <> -struct playrho::detail::TypeInfo -{ - /// @brief The specialized name for the playrho::Fixed64 type. - static constexpr const char* name = "Fixed64"; -}; -#endif /* PLAYRHO_INT128 */ - -#endif // PLAYRHO_FIXED_HPP diff --git a/Library/include/playrho/FixedLimits.hpp b/Library/include/playrho/FixedLimits.hpp deleted file mode 100644 index 2b588a3b4f..0000000000 --- a/Library/include/playrho/FixedLimits.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2023 Louis Langholtz https://github.com/louis-langholtz/PlayRho - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef PLAYRHO_FIXEDLIMITS_HPP -#define PLAYRHO_FIXEDLIMITS_HPP - -/// @file -/// @brief Specialization of @c std::numeric_limits for @c Fixed. - -#include - -/// @brief Template specialization of numeric limits for Fixed types. -/// @see https://en.cppreference.com/w/cpp/types/numeric_limits -template -class std::numeric_limits> -{ -public: - static constexpr bool is_specialized = true; ///< Type is specialized. - - /// @brief Gets the min value available for the type. - static constexpr playrho::Fixed min() noexcept { return playrho::Fixed::GetMin(); } - - /// @brief Gets the max value available for the type. - static constexpr playrho::Fixed max() noexcept { return playrho::Fixed::GetMax(); } - - /// @brief Gets the lowest value available for the type. - static constexpr playrho::Fixed lowest() noexcept { return playrho::Fixed::GetLowest(); } - - /// @brief Number of radix digits that can be represented. - static constexpr int digits = playrho::Fixed::WholeBits - 1; - - /// @brief Number of decimal digits that can be represented. - static constexpr int digits10 = playrho::Fixed::WholeBits - 1; - - /// @brief Number of decimal digits necessary to differentiate all values. - static constexpr int max_digits10 = 5; // TODO(lou): check this - - static constexpr bool is_signed = true; ///< Identifies signed types. - static constexpr bool is_integer = false; ///< Identifies integer types. - static constexpr bool is_exact = true; ///< Identifies exact type. - static constexpr int radix = 0; ///< Radix used by the type. - - /// @brief Gets the epsilon value for the type. - static constexpr playrho::Fixed32 epsilon() noexcept { return playrho::Fixed{0}; } // TODO(lou) - - /// @brief Gets the round error value for the type. - static constexpr playrho::Fixed32 round_error() noexcept { return playrho::Fixed{0}; } // TODO(lou) - - /// @brief One more than smallest negative power of the radix that's a valid - /// normalized floating-point value. - static constexpr int min_exponent = 0; - - /// @brief Smallest negative power of ten that's a valid normalized floating-point value. - static constexpr int min_exponent10 = 0; - - /// @brief One more than largest integer power of radix that's a valid finite - /// floating-point value. - static constexpr int max_exponent = 0; - - /// @brief Largest integer power of 10 that's a valid finite floating-point value. - static constexpr int max_exponent10 = 0; - - static constexpr bool has_infinity = true; ///< Whether can represent infinity. - static constexpr bool has_quiet_NaN = true; ///< Whether can represent quiet-NaN. - static constexpr bool has_signaling_NaN = false; ///< Whether can represent signaling-NaN. - static constexpr float_denorm_style has_denorm = denorm_absent; ///< Denorm style used. - static constexpr bool has_denorm_loss = false; ///< Has denorm loss amount. - - /// @brief Gets the infinite value for the type. - static constexpr playrho::Fixed infinity() noexcept { return playrho::Fixed::GetInfinity(); } - - /// @brief Gets the quiet NaN value for the type. - static constexpr playrho::Fixed quiet_NaN() noexcept { return playrho::Fixed::GetNaN(); } - - /// @brief Gets the signaling NaN value for the type. - static constexpr playrho::Fixed signaling_NaN() noexcept { return playrho::Fixed{0}; } - - /// @brief Gets the denorm value for the type. - static constexpr playrho::Fixed denorm_min() noexcept { return playrho::Fixed{0}; } - - static constexpr bool is_iec559 = false; ///< @brief Not an IEEE 754 floating-point type. - static constexpr bool is_bounded = true; ///< Type bounded: has limited precision. - static constexpr bool is_modulo = false; ///< Doesn't modulo arithmetic overflows. - - static constexpr bool traps = false; ///< Doesn't do traps. - static constexpr bool tinyness_before = false; ///< Doesn't detect tinyness before rounding. - static constexpr float_round_style round_style = round_toward_zero; ///< Rounds down. -}; - -#endif // PLAYRHO_FIXEDLIMITS_HPP diff --git a/Library/include/playrho/FixedMath.hpp b/Library/include/playrho/FixedMath.hpp deleted file mode 100644 index d05dbd2fbb..0000000000 --- a/Library/include/playrho/FixedMath.hpp +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Copyright (c) 2023 Louis Langholtz https://github.com/louis-langholtz/PlayRho - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef PLAYRHO_FIXEDMATH_HPP -#define PLAYRHO_FIXEDMATH_HPP - -/// @file -/// @brief Conventional math functions for the @c Fixed class template. - -#include - -#include - -namespace playrho { - -namespace detail { - -/// @brief Fixed point pi value. -template -constexpr auto FixedPi = Fixed{3.14159265358979323846264338327950288}; - -/// @brief Default iterations for @c exp function. -constexpr auto DefaultExpIterations = 6; - -/// @brief Default iterations for @c log function. -constexpr auto DefaultLogIterations = 6; - -/// @brief Default iterations for @c sin function. -constexpr auto DefaultSinIterations = 5; - -/// @brief Default iterations for @c cos function. -constexpr auto DefaultCosIterations = 5; - -/// @brief Default iterations for @c atan function. -constexpr auto DefaultAtanIterations = 5; - -/// @brief Computes the factorial. -constexpr auto factorial(std::int64_t n) -{ - // n! = n·(n – 1)·(n – 2) · · · 3·2·1 - auto res = n; - for (--n; n > 1; --n) - { - res *= n; - } - return res; -} - -/// @brief Computes Euler's number raised to the given power argument. -/// @note Uses Maclaurin series approximation. -/// @see https://en.cppreference.com/w/cpp/numeric/math/exp -/// @see https://en.wikipedia.org/wiki/Taylor_series -/// @see https://en.wikipedia.org/wiki/Exponentiation -/// @see https://en.wikipedia.org/wiki/Exponential_function -template -constexpr Fixed exp(Fixed arg) -{ - const auto doReciprocal = (arg < 0); - if (doReciprocal) - { - arg = -arg; - } - - // Maclaurin series approximation... - // e^x = sum(x^n/n!) for n =0 to infinity. - // e^x = 1 + x + x^2/2! + x^3/3! + ... - // Note: e^(x+y) = e^x * e^y. - // Note: convergence is slower for arg > 2 and overflow happens by i == 9 - auto pt = arg; - auto res = pt + 1; - auto ft = 1; - auto last = pt / ft; - for (auto i = 2; i < N; ++i) - { - // have to avoid unnecessarily overflowing... - last /= i; - last *= arg; - res += last; - } - return doReciprocal? 1 / res: res; -} - -/// @brief Computes the natural logarithm. -/// @note A better method may be explained in https://math.stackexchange.com/a/61236/408405 -/// @see https://en.cppreference.com/w/cpp/numeric/math/log -/// @see https://en.wikipedia.org/wiki/Natural_logarithm -template -Fixed log(Fixed arg) -{ - if (arg.isnan() || (arg < 0)) - { - return Fixed::GetNaN(); - } - if (arg == 0) - { - return Fixed::GetNegativeInfinity(); - } - if (arg == 1) - { - return Fixed{0}; - } - if (arg == Fixed::GetInfinity()) - { - return Fixed::GetInfinity(); - } - if (arg <= 2) - { - // ln(x) = sum((-1)^(n + 1) * (x - 1)^n / n) from n = 1 to infinity - // ln(x) = (x - 1) - (x - 1)^2/2 + (x - 1)^3/3 - (x - 1)^4/4 .... - arg -= 1; - auto res = arg; - auto sign = -1; - auto pt = arg; - for (auto i = 2; i < N; ++i) - { - pt *= arg; - res += sign * pt / i; - sign = -sign; - } - return res; - } - - // The following algorithm isn't as accurate as desired. - // Is there a better one? - // ln(x) = ((x - 1) / x) + ((x - 1) / x)^2/2 + ((x - 1) / x)^3/3 + ... - arg = (arg - 1) / arg; - auto pt = arg; - auto res = pt; - for (auto i = 2; i < N; ++i) - { - pt *= arg; - res += pt / i; - } - return res; -} - -/// @brief Computes the sine of the given argument via Maclaurin series approximation. -/// @see https://en.wikipedia.org/wiki/Taylor_series -template -constexpr Fixed sin(Fixed arg) -{ - // Maclaurin series approximation... - // sin x = sum((-1^n)*(x^(2n+1))/(2n+1)!) - // sin(2) = 0.90929742682 - // x - x^3/6 + x^5/120 - x^7/5040 + x^9/ - // 2 - 8/6 = 0.666 - // 2 - 8/6 + 32/120 = 0.9333 - // 2 - 8/6 + 32/120 - 128/5040 = 0.90793650793 - // 2 - 8/6 + 32/120 - 128/5040 + 512/362880 = 0.90934744268 - auto res = arg; - auto sgn = -1; - constexpr auto last = 2 * N + 1; - auto pt = arg; - auto ft = 1; - for (auto i = 3; i <= last; i += 2) - { - ft *= (i - 1) * i; - pt *= arg * arg; - const auto term = pt / ft; - res += sgn * term; - sgn = -sgn; - } - return res; -} - -/// @brief Computes the cosine of the given argument via Maclaurin series approximation. -/// @see https://en.wikipedia.org/wiki/Taylor_series -template -constexpr Fixed cos(Fixed arg) -{ - // Maclaurin series approximation... - // cos x = sum((-1^n)*(x^(2n))/(2n)!) - // cos(2) = -0.41614683654 - // 1 - 2^2/2 = -1 - // 1 - 2^2/2 + 2^4/24 = -0.3333 - // 1 - 2^2/2 + 2^4/24 - 2^6/720 = -0.422 - auto res = Fixed{1}; - auto sgn = -1; - constexpr auto last = 2 * N; - auto ft = 1; - auto pt = Fixed{1}; - for (auto i = 2; i <= last; i += 2) - { - ft *= (i - 1) * i; - pt *= arg * arg; - const auto term = pt / ft; - res += sgn * term; - sgn = -sgn; - } - return res; -} - -/// @brief Computes the arctangent of the given argument via Maclaurin series approximation. -/// @see https://en.cppreference.com/w/cpp/numeric/math/atan -/// @see https://en.wikipedia.org/wiki/Taylor_series -template -constexpr Fixed atan(Fixed arg) -{ - // Note: if (x > 0) then arctan(x) == Pi/2 - arctan(1/x) - // if (x < 0) then arctan(x) == -Pi/2 - arctan(1/x). - const auto doReciprocal = (abs(arg) > 1); - if (doReciprocal) - { - arg = 1 / arg; - } - - // Maclaurin series approximation... - // For |arg| <= 1, arg != +/- i - // If |arg| > 1 the result is too wrong which is why the reciprocal is done then. - auto res = arg; - auto sgn = -1; - const auto last = 2 * N + 1; - auto pt = arg; - for (auto i = 3; i <= last; i += 2) - { - pt *= arg * arg; - const auto term = pt / i; - res += sgn * term; - sgn = -sgn; - } - - if (doReciprocal) - { - return (arg > 0)? FixedPi / 2 - res: -FixedPi / 2 - res; - } - return res; -} - -/// @brief Computes the square root of a non-negative value. -/// @see https://en.wikipedia.org/wiki/Methods_of_computing_square_roots -template -constexpr auto ComputeSqrt(Fixed arg) -{ - auto temp = Fixed{1}; - auto tempSquared = Square(temp); - const auto greaterThanOne = arg > 1; - auto lower = greaterThanOne? Fixed{1}: arg; - auto upper = greaterThanOne? arg: Fixed{1}; - while (arg != tempSquared) - { - const auto mid = (lower + upper) / 2; - if (temp == mid) - { - break; - } - temp = mid; - tempSquared = Square(temp); - if (tempSquared > arg) - { - upper = temp; - } - else if (tempSquared < arg) - { - lower = temp; - } - } - return temp; -} - -/// @brief Normalizes the given angular argument. -template -inline auto AngularNormalize(Fixed angleInRadians) -{ - constexpr auto oneRotationInRadians = 2 * FixedPi; - - angleInRadians = fmod(angleInRadians, oneRotationInRadians); - if (angleInRadians > FixedPi) - { - // 190_deg becomes 190_deg - 360_deg = -170_deg - angleInRadians -= oneRotationInRadians; - } - else if (angleInRadians < -FixedPi) - { - // -200_deg becomes -200_deg + 360_deg = 100_deg - angleInRadians += oneRotationInRadians; - } - return angleInRadians; -} - -} // namespace detail - -/// @defgroup FixedMath Math Functions For Fixed Types -/// @brief Common Mathematical Functions For Fixed Types. -/// @note These functions directly compute their respective results. They don't convert -/// their inputs to a floating point type to use the standard math functions and then -/// convert those results back to the fixed point type. This has pros and cons. Some -/// pros are that: this won't suffer from the "non-determinism" inherent with different -/// hardware platforms potentially having different floating point or math library -/// implementations; this implementation won't suffer any overhead of converting between -/// the underlying type and a floating point type. On the con side however: this -/// implementation is unlikely to be anywhere near as tested as standard C++ math library -/// functions likely are; this implementation is unlikely to have anywhere near as much -/// performance tuning as standard library functions have had. -/// @see Fixed -/// @see https://en.cppreference.com/w/cpp/numeric/math -/// @{ - -/// @brief Computes the absolute value. -/// @see https://en.cppreference.com/w/cpp/numeric/math/fabs -template -constexpr Fixed abs(Fixed arg) -{ - return (arg >= 0)? arg: -arg; -} - -/// @brief Computes the value of the given number raised to the given power. -/// @note This implementation is for raising a given value to an integer power. -/// This may have significantly different performance than raising a value to a -/// non-integer power. -/// @see https://en.cppreference.com/w/cpp/numeric/math/pow -template -constexpr Fixed pow(Fixed value, int n) -{ - if (!n) - { - return Fixed{1}; - } - if (value == 0) - { - if (n > 0) - { - return Fixed{0}; - } - return Fixed::GetInfinity(); - } - if (value == 1) - { - return Fixed{1}; - } - if (value == Fixed::GetNegativeInfinity()) - { - if (n > 0) - { - if (n % 2 == 0) - { - return Fixed::GetInfinity(); - } - return Fixed::GetNegativeInfinity(); - } - return Fixed{0}; - } - if (value == Fixed::GetInfinity()) - { - return (n < 0)? Fixed{0}: Fixed::GetInfinity(); - } - const auto doReciprocal = (n < 0); - if (doReciprocal) - { - n = -n; - } - auto res = value; - for (; n > 1; --n) - { - res *= value; - } - return (doReciprocal)? 1 / res: res; -} - -/// @brief Truncates the given value. -/// @see https://en.cppreference.com/w/c/numeric/math/trunc -template -constexpr Fixed trunc(Fixed arg) -{ - return static_cast>(static_cast(arg)); -} - -/// @brief Next after function for Fixed types. -/// @see https://en.cppreference.com/w/cpp/numeric/math/nextafter -template -inline Fixed nextafter(Fixed from, Fixed to) noexcept -{ - if (from < to) - { - return static_cast>(from + Fixed::GetMin()); - } - if (from > to) - { - return static_cast>(from - Fixed::GetMin()); - } - return static_cast>(to); -} - -/// @brief Computes the remainder of the division of the given dividend by the given divisor. -/// @see https://en.cppreference.com/w/cpp/numeric/math/fmod -template -inline Fixed fmod(Fixed dividend, Fixed divisor) noexcept -{ - const auto quotient = dividend / divisor; - const auto integer = trunc(quotient); - const auto remainder = quotient - integer; - return remainder * divisor; -} - -/// @brief Square root's the given value. -/// @note This implementation isn't meant to be fast, only correct enough. -/// @note The IEEE standard (presumably IEC 60559), requires std::sqrt to be exact -/// to within half of a ULP for floating-point types (float, double). That sets a precedence -/// that puts a high expectation on this implementation for fixed-point types. -/// @note "Domain error" occurs if arg is less than zero. -/// @return Mathematical square root value of the given value or the NaN value. -/// @see https://en.cppreference.com/w/cpp/numeric/math/sqrt -template -inline auto sqrt(Fixed arg) -{ - if ((arg == Fixed{1}) || (arg == Fixed{0})) - { - return arg; - } - if (arg > Fixed{0}) - { - return detail::ComputeSqrt(arg); - } - // else arg < 0 or NaN... - return Fixed::GetNaN(); -} - -/// @brief Gets whether the given value is normal - i.e. not 0 nor infinite. -/// @see https://en.cppreference.com/w/cpp/numeric/math/isnormal -template -inline bool isnormal(Fixed arg) -{ - return arg != Fixed{0} && arg.isfinite(); -} - -/// @brief Computes the sine of the argument for Fixed types. -/// @see https://en.cppreference.com/w/cpp/numeric/math/sin -template -inline Fixed sin(Fixed arg) -{ - arg = detail::AngularNormalize(arg); - return detail::sin(arg); -} - -/// @brief Computes the cosine of the argument for Fixed types. -/// @see https://en.cppreference.com/w/cpp/numeric/math/cos -template -inline Fixed cos(Fixed arg) -{ - arg = detail::AngularNormalize(arg); - return detail::cos(arg); -} - -/// @brief Computes the arc tangent. -/// @see https://en.cppreference.com/w/cpp/numeric/math/atan -/// @return Value between -Pi / 2 and Pi / 2. -template -inline Fixed atan(Fixed arg) -{ - if (arg.isnan() || (arg == 0)) - { - return arg; - } - if (arg == Fixed::GetInfinity()) - { - return detail::FixedPi / 2; - } - if (arg == Fixed::GetNegativeInfinity()) - { - return -detail::FixedPi / 2; - } - return detail::atan(arg); -} - -/// @brief Computes the multi-valued inverse tangent. -/// @see https://en.cppreference.com/w/cpp/numeric/math/atan2 -/// @return Value between -Pi and +Pi inclusive. -template -inline Fixed atan2(Fixed y, Fixed x) -{ - // See https://en.wikipedia.org/wiki/Atan2 - // See https://en.wikipedia.org/wiki/Taylor_series - if (x > 0) - { - return atan(y / x); - } - if (x < 0) - { - return atan(y / x) + ((y >= 0)? +1: -1) * detail::FixedPi; - } - if (y > 0) - { - return +detail::FixedPi / 2; - } - if (y < 0) - { - return -detail::FixedPi / 2; - } - return Fixed::GetNaN(); -} - -/// @brief Computes the natural logarithm of the given argument. -/// @see https://en.cppreference.com/w/cpp/numeric/math/log -template -inline Fixed log(Fixed arg) -{ - static constexpr auto MaxForLowerIterations = 8; - static constexpr auto IterationsForSmaller = 36; - static constexpr auto IterationsForLarger = 96; - return (arg < MaxForLowerIterations) - ? detail::log(arg) - : detail::log(arg); -} - -/// @brief Computes the Euler number raised to the power of the given argument. -/// @see https://en.cppreference.com/w/cpp/numeric/math/exp -template -inline Fixed exp(Fixed arg) -{ - static constexpr auto MaxForLowerIterations = 2; - static constexpr auto IterationsForLarger = 24; - return (arg <= MaxForLowerIterations) - ? detail::exp(arg) - : detail::exp(arg); -} - -/// @brief Computes the value of the base number raised to the power of the exponent. -/// @see https://en.cppreference.com/w/cpp/numeric/math/pow -template -inline Fixed pow(Fixed base, Fixed exponent) -{ - if (exponent.isfinite()) - { - const auto intExp = static_cast(exponent); - if (intExp == exponent) - { - // fall back to integer version... - return pow(base, intExp); - } - } - - if (base < 0) - { - return Fixed::GetNaN(); - } - - const auto lnResult = log(base); - const auto expResult = exp(lnResult * exponent); - return expResult; -} - -/// @brief Computes the square root of the sum of the squares. -/// @see https://en.cppreference.com/w/cpp/numeric/math/hypot -template -inline Fixed hypot(Fixed x, Fixed y) -{ - return sqrt(x * x + y * y); -} - -/// @brief Rounds the given value. -/// @see https://en.cppreference.com/w/cpp/numeric/math/round -template -inline Fixed round(Fixed value) noexcept -{ - const auto tmp = value + (Fixed{1} / Fixed{2}); - const auto truncated = static_cast::value_type>(tmp); - return Fixed{truncated, 0}; -} - -/// @brief Determines whether the given value is negative. -/// @see https://en.cppreference.com/w/cpp/numeric/math/signbit -template -inline bool signbit(Fixed value) noexcept -{ - return value.getsign() < 0; -} - -/// @brief Gets whether the given value is not-a-number. -/// @see https://en.cppreference.com/w/cpp/numeric/math/isnan -template -constexpr bool isnan(Fixed value) noexcept -{ - return value.Compare(0) == Fixed::CmpResult::Incomparable; -} - -/// @brief Gets whether the given value is finite. -/// @see https://en.cppreference.com/w/cpp/numeric/math/isfinite -template -inline bool isfinite(Fixed value) noexcept -{ - return (value > Fixed::GetNegativeInfinity()) - && (value < Fixed::GetInfinity()); -} - -/// @brief Gets wether the given value is a positive or negative infinity. -/// @see https://en.cppreference.com/w/cpp/numeric/math/isinf -template -auto isinf(const Fixed& value) noexcept -{ - return (value == Fixed::GetInfinity()) // force newline - || (value == Fixed::GetNegativeInfinity()); -} - -/// @brief Gets the largest integer value not greater than the given value. -/// @see https://en.cppreference.com/w/cpp/numeric/math/floor. -template -auto floor(const Fixed& value) -{ - const auto tmp = trunc(value); - return (tmp > value)? (value - 1): tmp; -} - -/// @} - -} // namespace playrho - -#endif // PLAYRHO_FIXEDMATH_HPP diff --git a/UnitTests/Dump.cpp b/UnitTests/Dump.cpp index f4f790f10f..4ab1afbd72 100644 --- a/UnitTests/Dump.cpp +++ b/UnitTests/Dump.cpp @@ -64,26 +64,6 @@ struct Vals { } }; -template <> -struct Vals { - static inline const char* GetGravity() - { - return "-9\\.799999952316284e\\+00f"; - } -}; - -#ifndef _WIN32 - -template <> -struct Vals { - static inline const char* GetGravity() - { - return "-9\\.799999952316284e\\+00f"; - } -}; - -#endif /* !_WIN32 */ - TEST(Dump, EmptyWorld) { std::stringstream buf; diff --git a/UnitTests/Fixed.cpp b/UnitTests/Fixed.cpp deleted file mode 100644 index a5b6749edd..0000000000 --- a/UnitTests/Fixed.cpp +++ /dev/null @@ -1,1080 +0,0 @@ -/* - * Copyright (c) 2023 Louis Langholtz https://github.com/louis-langholtz/PlayRho - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include "UnitTests.hpp" - -#include -#include -#include -#include - -#include - -using namespace playrho; - -TEST(Fixed32, ByteSizeIs4) -{ - // Check size at test runtime instead of compile-time via static_assert to avoid stopping - // builds and to report actual size rather than just reporting that expected size is wrong. - EXPECT_EQ(sizeof(Fixed32), std::size_t(4)); -} - -TEST(Fixed32, GetTypeName) -{ - EXPECT_STREQ(GetTypeName(), "Fixed32"); -} - -#ifdef PLAYRHO_INT128 -TEST(Fixed64, ByteSizeIs8) -{ - EXPECT_EQ(sizeof(Fixed64), std::size_t(8)); -} - -TEST(Fixed64, GetTypeName) -{ - EXPECT_STREQ(GetTypeName(), "Fixed64"); -} -#endif - -// Tests of Fixed::GetFromUnsignedInt(v) - -#define DECL_GET_FROM_UNSIGNED_INT_TEST(type) \ -TEST(type, GetFromUnsignedInt) \ -{ \ - EXPECT_EQ(type::GetFromUnsignedInt(0u), 0 * type::ScaleFactor); \ - EXPECT_EQ(type::GetFromUnsignedInt(1u), 1 * type::ScaleFactor); \ - EXPECT_EQ(type::GetFromUnsignedInt(2u), 2 * type::ScaleFactor); \ -} - -DECL_GET_FROM_UNSIGNED_INT_TEST(Fixed32) -#ifdef PLAYRHO_INT128 -DECL_GET_FROM_UNSIGNED_INT_TEST(Fixed64) -#endif - -// Tests of Fixed::GetFromSignedInt(v) - -#define DECL_GET_FROM_SIGNED_INT_TEST(type) \ -TEST(type, GetFromSignedInt) \ -{ \ - EXPECT_EQ(type::GetFromSignedInt( 0), 0 * type::ScaleFactor); \ - EXPECT_EQ(type::GetFromSignedInt( 1), 1 * type::ScaleFactor); \ - EXPECT_EQ(type::GetFromSignedInt( 2), 2 * type::ScaleFactor); \ - EXPECT_EQ(type::GetFromSignedInt(-1), -1 * type::ScaleFactor); \ - EXPECT_EQ(type::GetFromSignedInt(-2), -2 * type::ScaleFactor); \ -} - -DECL_GET_FROM_SIGNED_INT_TEST(Fixed32) -#ifdef PLAYRHO_INT128 -DECL_GET_FROM_SIGNED_INT_TEST(Fixed64) -#endif - -// Tests of Fixed::GetFromFloat(v) - -#define DECL_GET_FROM_FLOAT_TEST(type) \ -TEST(type, GetFromFloat) \ -{ \ - EXPECT_EQ(type::GetFromFloat( 0.0), 0 * type::ScaleFactor); \ - EXPECT_EQ(type::GetFromFloat( 1.0), 1 * type::ScaleFactor); \ - EXPECT_EQ(type::GetFromFloat( 2.0), 2 * type::ScaleFactor); \ - EXPECT_EQ(type::GetFromFloat(-1.0), -1 * type::ScaleFactor); \ - EXPECT_EQ(type::GetFromFloat(-2.0), -2 * type::ScaleFactor); \ - EXPECT_EQ(type::GetFromFloat(-4.7), static_cast(-4.7 * type::ScaleFactor)); \ - const auto long_double_max = std::numeric_limits::max(); \ - const auto fixed_infinity = type::GetInfinity(); \ - const auto fixed_infinity_as_vt = *reinterpret_cast(&fixed_infinity); \ - EXPECT_EQ(type::GetFromFloat( long_double_max), fixed_infinity_as_vt); \ - EXPECT_EQ(type::GetFromFloat(-long_double_max), -fixed_infinity_as_vt); \ - EXPECT_EQ(type::GetFromFloat( std::numeric_limits::infinity()), fixed_infinity_as_vt); \ - EXPECT_EQ(type::GetFromFloat(-std::numeric_limits::infinity()), -fixed_infinity_as_vt); \ -} - -DECL_GET_FROM_FLOAT_TEST(Fixed32) -#ifdef PLAYRHO_INT128 -DECL_GET_FROM_FLOAT_TEST(Fixed64) -#endif - -// Tests of Fixed::Fixed(v) and comparisons - -#define DECL_INT_CONSTRUCTION_AND_COMPARE_TEST(type) \ -TEST(type, IntConstructionAndCompare) \ -{ \ - EXPECT_EQ(type( 0), type( 0)); \ - EXPECT_LT(type( 0), type( 1)); \ - EXPECT_GT(type( 0), type(-1)); \ - EXPECT_EQ(type(-10), type(-10)); \ - EXPECT_LT(type(-10), type( -9)); \ - EXPECT_GT(type(-10), type(-11)); \ -} - -DECL_INT_CONSTRUCTION_AND_COMPARE_TEST(Fixed32) -#ifdef PLAYRHO_INT128 -DECL_INT_CONSTRUCTION_AND_COMPARE_TEST(Fixed64) -#endif - -// Tests of isfinite(Fixed) - -#define DECL_ISFINITE_TEST(type) \ -TEST(type, isfinite) \ -{ \ - EXPECT_TRUE(isfinite(type(0))); \ - EXPECT_FALSE(isfinite( type::GetInfinity())); \ - EXPECT_FALSE(isfinite(-type::GetInfinity())); \ - EXPECT_FALSE(isfinite(type::GetNaN())); \ -} - -DECL_ISFINITE_TEST(Fixed32) -#ifdef PLAYRHO_INT128 -DECL_ISFINITE_TEST(Fixed64) -#endif - -// Tests of isnan(Fixed) - -#define DECL_ISNAN_TEST(type) \ -TEST(type, isnan) \ -{ \ - EXPECT_FALSE(isnan(type( 0))); \ - EXPECT_FALSE(isnan(type( 1))); \ - EXPECT_FALSE(isnan(type(-1))); \ - EXPECT_FALSE(isnan( type::GetInfinity())); \ - EXPECT_FALSE(isnan(-type::GetInfinity())); \ - EXPECT_FALSE(isnan( type::GetNegativeInfinity())); \ - EXPECT_TRUE(isnan(type::GetNaN())); \ - EXPECT_TRUE(isnan(type(std::numeric_limits::quiet_NaN()))); \ - EXPECT_TRUE(isnan(type(std::numeric_limits::signaling_NaN()))); \ - EXPECT_TRUE(isnan(type(std::numeric_limits::quiet_NaN()))); \ - EXPECT_TRUE(isnan(type(std::numeric_limits::signaling_NaN()))); \ - EXPECT_TRUE(isnan(type(std::numeric_limits::quiet_NaN()))); \ - EXPECT_TRUE(isnan(type(std::numeric_limits::signaling_NaN()))); \ -} - -DECL_ISNAN_TEST(Fixed32) -#ifdef PLAYRHO_INT128 -DECL_ISNAN_TEST(Fixed64) -#endif - -// Regular tests - -TEST(Fixed32, IntCast) -{ - EXPECT_EQ(static_cast(Fixed32( 0)), 0); - EXPECT_EQ(static_cast(Fixed32(-1)), -1); - EXPECT_EQ(static_cast(Fixed32(-2)), -2); - EXPECT_EQ(static_cast(Fixed32(+1)), +1); - EXPECT_EQ(static_cast(Fixed32(+2)), +2); -} - -#ifdef PLAYRHO_INT128 -TEST(Fixed64, IntCast) -{ - EXPECT_EQ(static_cast(Fixed64( 0)), 0); - EXPECT_EQ(static_cast(Fixed64(-1)), -1); - EXPECT_EQ(static_cast(Fixed64(-2)), -2); - EXPECT_EQ(static_cast(Fixed64(+1)), +1); - EXPECT_EQ(static_cast(Fixed64(+2)), +2); -} -#endif - -TEST(Fixed32, FloatCast) -{ - EXPECT_EQ(static_cast(Fixed32( 0)), 0.0f); - EXPECT_EQ(static_cast(Fixed32(-1)), -1.0f); - EXPECT_EQ(static_cast(Fixed32(+1)), +1.0f); -} - -#ifdef PLAYRHO_INT128 -TEST(Fixed64, FloatCast) -{ - EXPECT_EQ(static_cast(Fixed64( 0)), 0.0f); - EXPECT_EQ(static_cast(Fixed64(-1)), -1.0f); - EXPECT_EQ(static_cast(Fixed64(+1)), +1.0f); -} -#endif - -TEST(Fixed32, DoubleCast) -{ - EXPECT_EQ(static_cast(Fixed32( 0)), 0.0); - EXPECT_EQ(static_cast(Fixed32(-1)), -1.0); - EXPECT_EQ(static_cast(Fixed32(+1)), +1.0); -} - -#ifdef PLAYRHO_INT128 -TEST(Fixed64, DoubleCast) -{ - EXPECT_EQ(static_cast(Fixed64( 0)), 0.0); - EXPECT_EQ(static_cast(Fixed64(-1)), -1.0); - EXPECT_EQ(static_cast(Fixed64(+1)), +1.0); -} -#endif - -TEST(Fixed32, FloatConstruction) -{ - EXPECT_EQ(Fixed32(0.0), 0.0); - EXPECT_EQ(Fixed32(-1.0), -1.0); - EXPECT_EQ(Fixed32(+1.0), +1.0); - - EXPECT_EQ(Fixed32(std::numeric_limits::infinity()), Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32(-std::numeric_limits::infinity()), -Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32(-std::numeric_limits::infinity()), Fixed32::GetNegativeInfinity()); - EXPECT_TRUE(isnan(Fixed32(std::numeric_limits::quiet_NaN()))); - EXPECT_TRUE(isnan(Fixed32(std::numeric_limits::signaling_NaN()))); - - const auto range = 30000; - for (auto i = -range; i < range; ++i) - { - EXPECT_EQ(Fixed32(static_cast(i)), i); - EXPECT_EQ(Fixed32(float(i)), i); - EXPECT_EQ(Fixed32(float(i)), Fixed32(i)); - } -} - -TEST(Fixed32, GetMin) -{ - EXPECT_NEAR(static_cast(Fixed32::GetMin()), 0.001953125, 0.00001); -} - -TEST(Fixed32, GetMax) -{ - EXPECT_NEAR(static_cast(Fixed32::GetMax()), 4194303.99609375, - 0.0001); -} - -TEST(Fixed32, limits) -{ - EXPECT_NEAR(static_cast(std::numeric_limits::max()), 4194303.99609375, - 0.0); - EXPECT_NEAR(static_cast(std::numeric_limits::lowest()), -4194303.99609375, - 0.0); - EXPECT_NEAR(static_cast(std::numeric_limits::min()), 0.001953125, - 0.0); -} - -#ifdef PLAYRHO_INT128 -TEST(Fixed64, GetMin) -{ - EXPECT_NEAR(static_cast(Fixed64::GetMin()), 5.9604644775390625e-08, 0.0); -} - -TEST(Fixed64, GetMax) -{ - EXPECT_NEAR(static_cast(Fixed64::GetMax()), 549755813888, 0.0); -} -#endif - -TEST(Fixed32, Equals) -{ - EXPECT_TRUE(Fixed32(12) == Fixed32(12.0f)); - EXPECT_FALSE(std::numeric_limits::quiet_NaN() == std::numeric_limits::quiet_NaN()); -} - -TEST(Fixed32, NotEquals) -{ - EXPECT_TRUE(Fixed32(-302) != Fixed32(12.0f)); - EXPECT_FALSE(Fixed32(-302) != Fixed32(-302)); - EXPECT_TRUE(std::numeric_limits::quiet_NaN() != std::numeric_limits::quiet_NaN()); -} - -TEST(Fixed32, LessThan) -{ - EXPECT_TRUE(Fixed32(-302) < Fixed32(12.0f)); - EXPECT_TRUE(Fixed32(40) < Fixed32(44)); - EXPECT_FALSE(Fixed32(76) < Fixed32(31)); - EXPECT_TRUE(Fixed32(0.001) < Fixed32(0.002)); - EXPECT_TRUE(Fixed32(0.000) < Fixed32(0.01)); -} - -TEST(Fixed32, GreaterThan) -{ - EXPECT_FALSE(Fixed32(-302) > Fixed32(12.0f)); - EXPECT_FALSE(Fixed32(40) > Fixed32(44)); - EXPECT_TRUE(Fixed32(76) > Fixed32(31)); -} - -TEST(Fixed32, Addition) -{ - for (auto val = 0; val < 100; ++val) - { - Fixed32 a{val}; - Fixed32 b{val}; - EXPECT_EQ(a + b, Fixed32(val * 2)); - } -} - -TEST(Fixed32, InfinityPlusValidIsInfinity) -{ - EXPECT_EQ(Fixed32::GetInfinity() + 0, Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() + 1, Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() + 100, Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() + -1, Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() + -100, Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() + Fixed32::GetInfinity(), Fixed32::GetInfinity()); -} - -TEST(Fixed32, EqualSubtraction) -{ - for (auto val = 0; val < 100; ++val) - { - Fixed32 a{val}; - Fixed32 b{val}; - EXPECT_EQ(a - b, Fixed32(0)); - } -} - -TEST(Fixed32, OppositeSubtraction) -{ - for (auto val = 0; val < 100; ++val) - { - Fixed32 a{-val}; - Fixed32 b{val}; - EXPECT_EQ(a - b, Fixed32(val * -2)); - } -} - -TEST(Fixed32, Multiplication) -{ - for (auto val = 0; val < 100; ++val) - { - Fixed32 a{val}; - EXPECT_EQ(a * a, Fixed32(val * val)); - } - EXPECT_EQ(Fixed32(9) * Fixed32(3), Fixed32(27)); - EXPECT_EQ(Fixed32(-5) * Fixed32(-4), Fixed32(20)); - EXPECT_EQ(Fixed32(0.5) * Fixed32(0.5), Fixed32(0.25)); - EXPECT_EQ(RoundOff(Fixed32(-0.05) * Fixed32(0.05), 1000), RoundOff(Fixed32(-0.0025), 1000)); - EXPECT_EQ(RoundOff(Fixed32(Pi) * 2, 100), RoundOff(Fixed32(Pi * 2), 100)); - EXPECT_EQ(Fixed32(181) * Fixed32(181), Fixed32(32761)); -} - -TEST(Fixed32, Division) -{ - for (auto val = 1; val < 100; ++val) - { - Fixed32 a{val}; - EXPECT_EQ(a / a, Fixed32(1)); - } - EXPECT_EQ(Fixed32(9) / Fixed32(3), Fixed32(3)); - EXPECT_EQ(Fixed32(81) / Fixed32(9), Fixed32(9)); - EXPECT_EQ(Fixed32(-10) / Fixed32(2), Fixed32(-5)); - EXPECT_EQ(Fixed32(1) / Fixed32(2), Fixed32(0.5)); - EXPECT_EQ(Fixed32(7) / Fixed32(3), Fixed32(7.0/3.0)); - - // Confirm int divided by Fixed32 gets promoted to Fixed32 divided by Fixed32 - EXPECT_EQ(1 / Fixed32(2), Fixed32(0.5)); - EXPECT_EQ(2 / Fixed32(2), Fixed32(1)); - EXPECT_EQ(3 / Fixed32(2), Fixed32(1.5)); -} - -TEST(Fixed32, log) -{ - ASSERT_FALSE(isfinite(static_cast(log(0.0)))); - EXPECT_FALSE(isfinite(static_cast(log(Fixed32(0))))); - - ASSERT_TRUE(isnan(static_cast(log(-1.0)))); - EXPECT_TRUE(isnan(static_cast(log(Fixed32(-1))))); - - ASSERT_NEAR(static_cast(log(0.1)), -2.3025850929940455, 0.01); - EXPECT_NEAR(static_cast(log(Fixed32(0.1))), -2.3025850929940455, 0.051); - - ASSERT_NEAR(static_cast(log(0.5)), -0.69314718055994529, 0.01); - EXPECT_NEAR(static_cast(log(Fixed32(0.5))), -0.69314718055994529, 0.01); - - ASSERT_NEAR(static_cast(log(1.0)), 0.0, 0.01); - EXPECT_NEAR(static_cast(log(Fixed32(1.0))), 0.0, 0.01); - - ASSERT_NEAR(static_cast(log(1.5)), 0.40546510810816438, 0.01); - EXPECT_NEAR(static_cast(log(Fixed32(1.5))), 0.40546510810816438, 0.01); - - ASSERT_NEAR(static_cast(log(2.0)), 0.69314718055994529, 0.01); - EXPECT_NEAR(static_cast(log(Fixed32(2.0))), 0.69314718055994529, 0.012); - - ASSERT_NEAR(static_cast(log(2.1)), 0.74193734472937733, 0.01); - EXPECT_NEAR(static_cast(log(Fixed32(2.1))), 0.74193734472937733, 0.0096); - - ASSERT_NEAR(static_cast(log(2.75)), 1.0116009116784799, 0.01); - EXPECT_NEAR(static_cast(log(Fixed32(2.75))), 0.994140625, 0.0001); - - ASSERT_NEAR(static_cast(log(4.5)), 1.5040773967762742, 0.01); - EXPECT_NEAR(static_cast(log(Fixed32(4.5))), 1.5040773967762742, 0.028); - - ASSERT_NEAR(static_cast(log(31.21)), 3.440738556282688, 0.01); - EXPECT_NEAR(static_cast(log(Fixed32(31.21))), log(31.21), 0.25); - - // Error gets pretty bad... - ASSERT_NEAR(static_cast(log(491.721)), 6.1979114824747752, 0.01); - EXPECT_NEAR(static_cast(log(Fixed32(491.721))), log(491.721), 1.517); - - EXPECT_EQ(static_cast(log(Fixed32::GetInfinity())), log(std::numeric_limits::infinity())); -} - -TEST(Fixed32, exp) -{ - EXPECT_NEAR(static_cast(exp(Fixed32(0))), exp(0.0), 0.01); - EXPECT_NEAR(static_cast(exp(Fixed32(0.4))), exp(0.4), 0.02); - EXPECT_NEAR(static_cast(exp(Fixed32(0.9))), exp(0.9), 0.02); - EXPECT_NEAR(static_cast(exp(Fixed32(1.0))), exp(1.0), 0.02); - - ASSERT_NEAR(static_cast(exp(1.34)), 3.8190435053663361, 0.001); - EXPECT_NEAR(static_cast(exp(Fixed32(1.34))), exp(1.34), 0.019); - - ASSERT_NEAR(static_cast(exp(2.5)), 12.182493960703473, 0.01); - EXPECT_NEAR(static_cast(exp(Fixed32(2.5))), exp(2.5), 0.04); - - ASSERT_NEAR(static_cast(exp(3.15)), 23.336064580942711, 0.2); - EXPECT_NEAR(static_cast(exp(Fixed32(3.15))), 23.23828125, 0.01); - EXPECT_NEAR(static_cast(exp(Fixed32(3.15))), exp(3.15), 0.1); - - ASSERT_NEAR(static_cast(exp(4.8)), 121.51041751873485, 0.2); - EXPECT_NEAR(static_cast(exp(Fixed32(4.8))), 121.19140625, 0.01); - EXPECT_NEAR(static_cast(exp(Fixed32(4.8))), exp(4.8), 0.4); - - ASSERT_NEAR(static_cast(exp(7.1)), 1211.9670744925763, 0.2); - EXPECT_NEAR(static_cast(exp(Fixed32(7.1))), 1210.525390625, 0.01); - EXPECT_NEAR(static_cast(exp(Fixed32(7.1))), exp(7.1), 1.6); - - ASSERT_NEAR(static_cast(exp(8.9)), 7331.9735391559952, 0.2); - EXPECT_NEAR(static_cast(exp(Fixed32(8.9))), 7318.447265625, 0.01); - EXPECT_NEAR(static_cast(exp(Fixed32(8.9))), exp(8.9), 13.55); - - ASSERT_NEAR(static_cast(exp(10.1)), 24343.009424408381, 0.2); - EXPECT_NEAR(static_cast(exp(Fixed32(10.1))), 24322.119140625, 0.01); - EXPECT_NEAR(static_cast(exp(Fixed32(10.1))), exp(10.1), 22.0); - - ASSERT_NEAR(static_cast(exp(12.5)), 268337.28652087448, 0.2); - EXPECT_NEAR(static_cast(exp(Fixed32(12.5))), 267662.830078125, 0.01); - - ASSERT_NEAR(static_cast(exp(-1.0)), 0.36787944117144233, 0.0001); - EXPECT_NEAR(static_cast(exp(Fixed32(-1))), 0.36787944117144233, 0.001); - - ASSERT_NEAR(static_cast(exp(-2.0)), 0.1353352832366127, 0.0001); - EXPECT_NEAR(static_cast(exp(Fixed32(-2))), 0.13671875, 0.001); - - ASSERT_NEAR(static_cast(exp(-4.0)), 0.018315638888734179, 0.0001); - EXPECT_NEAR(static_cast(exp(Fixed32(-4))), 0.021484375, 0.001); -} - -TEST(Fixed32, intpow) -{ - ASSERT_NEAR(static_cast(pow(0.0, 0)), 1.0, 0.0); - ASSERT_NEAR(static_cast(pow(0.0, +1)), 0.0, 0.0); - ASSERT_FALSE(isfinite(static_cast(pow(0.0, -1)))); - - EXPECT_NEAR(static_cast(pow(Fixed32(0), 0)), 1.0, 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(0), +1)), 0.0, 0.0); - EXPECT_FALSE(isfinite(static_cast(pow(Fixed32(0), -1)))); - - EXPECT_EQ(static_cast(pow(Fixed32::GetNegativeInfinity(), -1)), - pow(-std::numeric_limits::infinity(), -1)); - EXPECT_EQ(static_cast(pow(Fixed32::GetNegativeInfinity(), +1)), - pow(-std::numeric_limits::infinity(), +1)); - EXPECT_EQ(static_cast(pow(Fixed32::GetNegativeInfinity(), +2)), - pow(-std::numeric_limits::infinity(), +2)); - EXPECT_EQ(static_cast(pow(Fixed32::GetInfinity(), +2)), - pow(std::numeric_limits::infinity(), +2)); - EXPECT_EQ(static_cast(pow(Fixed32::GetInfinity(), -2)), - pow(std::numeric_limits::infinity(), -2)); - - EXPECT_NEAR(static_cast(pow(Fixed32(0), 1)), pow(0.0, 1), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(0), 0)), pow(0.0, 0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(1), 0)), pow(1.0, 0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(1), +44)), pow(1.0, +44), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(1), -44)), pow(1.0, -44), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+1), +1)), pow(+1.0, +1), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(-1), +1)), pow(-1.0, +1), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+1), -1)), pow(+1.0, -1), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(-1), -1)), pow(-1.0, -1), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+2), +1)), pow(+2.0, +1), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+3), +1)), pow(+3.0, +1), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+3), +2)), pow(+3.0, +2), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+3), +3)), pow(+3.0, +3), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+9), +2)), pow(+9.0, +2), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+9), -1)), pow(+9.0, -1), 0.01); - EXPECT_NEAR(static_cast(pow(Fixed32(+3), -1)), pow(+3.0, -1), 0.01); - EXPECT_NEAR(static_cast(pow(Fixed32(+9), -2)), pow(+9.0, -2), 0.01); - EXPECT_NEAR(static_cast(pow(Fixed32(+3), -2)), pow(+3.0, -2), 0.01); - EXPECT_NEAR(static_cast(pow(Fixed32(+10), -2)), pow(+10.0, -2), 0.01); -} - -TEST(Fixed32, regpow) -{ - ASSERT_NEAR(pow(0.0, 0.0), 1.0, 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(0), Fixed32(0))), pow(0.0, 0.0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(1), Fixed32(0))), pow(1.0, 0.0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(1), Fixed32(+44.2))), pow(1.0, +44.2), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(1), Fixed32(-44.2))), pow(1.0, -44.2), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+1), Fixed32(+1))), pow(+1.0, +1.0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(-1), Fixed32(+1))), pow(-1.0, +1.0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+1), Fixed32(-1))), pow(+1.0, -1.0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(-1), Fixed32(-1))), pow(-1.0, -1.0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+2), Fixed32(+1))), pow(+2.0, +1.0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+3), Fixed32(+1))), pow(+3.0, +1.0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+3), Fixed32(+2))), pow(+3.0, +2.0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+3), Fixed32(+3))), pow(+3.0, +3.0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+9), Fixed32(+2))), pow(+9.0, +2.0), 0.0); - EXPECT_NEAR(static_cast(pow(Fixed32(+9), Fixed32(-1))), pow(+9.0, -1.0), 0.01); - EXPECT_NEAR(static_cast(pow(Fixed32(+3), Fixed32(-1))), pow(+3.0, -1.0), 0.01); - EXPECT_NEAR(static_cast(pow(Fixed32(+9), Fixed32(-2))), pow(+9.0, -2.0), 0.01); - EXPECT_NEAR(static_cast(pow(Fixed32(+3), Fixed32(-2))), pow(+3.0, -2.0), 0.01); - EXPECT_NEAR(static_cast(pow(Fixed32(+10), Fixed32(-2))), pow(+10.0, -2.0), 0.01); - EXPECT_NEAR(static_cast(pow(Fixed32(-10), Fixed32(-2))), pow(-10.0, -2.0), 0.01); - - EXPECT_NEAR(static_cast(pow(Fixed32(+4), Fixed32(+2.3))), pow(+4.0, +2.3), 0.97); - EXPECT_NEAR(static_cast(pow(Fixed32(+4), Fixed32(-2.3))), pow(+4.0, -2.3), 0.1); - - EXPECT_NEAR(static_cast(pow(Fixed32(+3.1), Fixed32(+2.3))), pow(+3.1, +2.3), 0.75); - EXPECT_NEAR(static_cast(pow(Fixed32(+3.1), Fixed32(-2.3))), pow(+3.1, -2.3), 0.1); - - EXPECT_NEAR(static_cast(pow(Fixed32(+3.1), Fixed32(+4.3))), pow(+3.1, +4.3), 12.3); - EXPECT_NEAR(static_cast(pow(Fixed32(+3.1), Fixed32(-4.3))), pow(+3.1, -4.3), 0.3); - - EXPECT_EQ(isnan(static_cast(pow(Fixed32(-4), Fixed32(+2.3)))), isnan(pow(-4.0, +2.3))); - EXPECT_EQ(isnan(static_cast(pow(Fixed32(-4), Fixed32(-2.3)))), isnan(pow(-4.0, -2.3))); -} - -TEST(Fixed32, sqrt) -{ - const auto tolerance = static_cast(Fixed32::GetMin()); - - EXPECT_TRUE(isnan(sqrt(Fixed32{-1}))); - EXPECT_TRUE(isnan(sqrt(Fixed32::GetNaN()))); - EXPECT_EQ(sqrt(Fixed32{0}), Fixed32{0}); - EXPECT_EQ(sqrt(Fixed32::GetInfinity()), Fixed32::GetInfinity()); - EXPECT_EQ(sqrt(Fixed32::GetMin()), Fixed32(0.046875)); - EXPECT_EQ(Square(sqrt(Fixed32::GetMin())), Fixed32::GetMin()); - EXPECT_EQ(sqrt(Fixed32{1}), Fixed32{1}); - EXPECT_NEAR(static_cast(sqrt(Fixed32{0.25})), 0.5, 0.0); - EXPECT_NEAR(static_cast(sqrt(Fixed32{0.0625})), 0.25, tolerance); - - for (auto i = 0; i < 10000; ++i) - { - EXPECT_NEAR(static_cast(sqrt(Fixed32(i))), sqrt(double(i)), 0.01); - } - - for (auto v = 1.0; v > 0.0; v /= 1.1) - { - const auto fixedv = Fixed32{v}; - if (fixedv == Fixed32{0}) - { - break; - } - EXPECT_LE(abs(Square(sqrt(fixedv)) - fixedv), Fixed32::GetMin()); - EXPECT_NEAR(static_cast(sqrt(fixedv)), sqrt(static_cast(fixedv)), tolerance * 5); - } - const auto maxV = static_cast(std::numeric_limits::max()); - for (auto v = 1.0; v < maxV; v *= 1.1) - { - const auto fixedv = Fixed32{v}; - EXPECT_NEAR(static_cast(sqrt(fixedv)), sqrt(v), tolerance); - //EXPECT_LE(abs(Square(sqrt(fixedv)) - fixedv), Fixed32::GetMin() * 5); - } -} - -TEST(Fixed32, hypot) -{ - for (auto i = 0; i < 100; ++i) - { - for (auto j = 0; i < 100; ++i) - { - EXPECT_NEAR(static_cast(hypot(Fixed32(i), Fixed32(j))), - hypot(double(i), double(j)), 0.01); - } - } -} - -#ifdef PLAYRHO_INT128 -TEST(Fixed64, hypot) -{ - for (auto i = 0; i < 100; ++i) - { - for (auto j = 0; i < 100; ++i) - { - EXPECT_NEAR(static_cast(hypot(Fixed64(i), Fixed64(j))), - hypot(double(i), double(j)), 0.001); - } - } -} -#endif - -TEST(Fixed32, sin) -{ - constexpr auto pi = double{3.14159265358979323846264338327950288}; - EXPECT_NEAR(static_cast(sin(Fixed32(0))), 0.0, 0.005); - EXPECT_NEAR(static_cast(sin(Fixed32(+pi/4))), std::sin(+pi/4), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(-pi/4))), std::sin(-pi/4), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(+1))), std::sin(+1), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(-1))), std::sin(-1), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(+pi/2))), std::sin(+pi/2), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(-pi/2))), std::sin(-pi/2), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(+2))), std::sin(+2), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(-2))), std::sin(-2), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(+3))), std::sin(+3), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(-3))), std::sin(-3), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(+pi))), std::sin(+pi), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(-pi))), std::sin(-pi), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(+4))), std::sin(+4), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(-4))), std::sin(-4), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(+5))), std::sin(+5), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(-5))), std::sin(-5), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(+pi*2))), std::sin(+pi*2), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(-pi*2))), std::sin(-pi*2), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(+8))), std::sin(+8), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(-8))), std::sin(-8), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(+10))), std::sin(+10), 0.015); - EXPECT_NEAR(static_cast(sin(Fixed32(-10))), std::sin(-10), 0.015); -} - -#ifdef PLAYRHO_INT128 -TEST(Fixed64, sin) -{ - constexpr auto pi = double{3.14159265358979323846264338327950288}; - EXPECT_NEAR(static_cast(sin(Fixed64(0))), 0.0, 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(+pi/4))), std::sin(+pi/4), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(-pi/4))), std::sin(-pi/4), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(+1))), std::sin(+1), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(-1))), std::sin(-1), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(+pi/2))), std::sin(+pi/2), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(-pi/2))), std::sin(-pi/2), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(+2))), std::sin(+2), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(-2))), std::sin(-2), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(+3))), std::sin(+3), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(-3))), std::sin(-3), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(+pi))), std::sin(+pi), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(-pi))), std::sin(-pi), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(+4))), std::sin(+4), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(-4))), std::sin(-4), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(+5))), std::sin(+5), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(-5))), std::sin(-5), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(+pi*2))), std::sin(+pi*2), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(-pi*2))), std::sin(-pi*2), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(+8))), std::sin(+8), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(-8))), std::sin(-8), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(+10))), std::sin(+10), 0.002); - EXPECT_NEAR(static_cast(sin(Fixed64(-10))), std::sin(-10), 0.002); -} -#endif - -TEST(Fixed32, cos) -{ - constexpr auto pi = double{3.14159265358979323846264338327950288}; - EXPECT_NEAR(static_cast(cos(Fixed32(0))), 1.0, 0.01); - EXPECT_NEAR(static_cast(cos(Fixed32(+1))), std::cos(+1.0), 0.015); - EXPECT_NEAR(static_cast(cos(Fixed32(-1))), std::cos(-1.0), 0.015); - EXPECT_NEAR(static_cast(cos(Fixed32(+2))), std::cos(+2.0), 0.015); - EXPECT_NEAR(static_cast(cos(Fixed32(-2))), std::cos(-2.0), 0.015); - EXPECT_NEAR(static_cast(cos(Fixed32(+pi/2))), std::cos(+pi/2), 0.015); - EXPECT_NEAR(static_cast(cos(Fixed32(-pi/2))), std::cos(-pi/2), 0.015); - EXPECT_NEAR(static_cast(cos(Fixed32(+3))), std::cos(+3.0), 0.015); - EXPECT_NEAR(static_cast(cos(Fixed32(-3))), std::cos(-3.0), 0.015); - EXPECT_NEAR(static_cast(cos(Fixed32(+8))), std::cos(+8), 0.015); - EXPECT_NEAR(static_cast(cos(Fixed32(-8))), std::cos(-8), 0.015); - EXPECT_NEAR(static_cast(cos(Fixed32(+10))), std::cos(+10), 0.015); - EXPECT_NEAR(static_cast(cos(Fixed32(-10))), std::cos(-10), 0.015); -} - -#ifdef PLAYRHO_INT128 -TEST(Fixed64, cos) -{ - constexpr auto pi = double{3.14159265358979323846264338327950288}; - EXPECT_NEAR(static_cast(cos(Fixed64(0))), 1.0, 0.01); - EXPECT_NEAR(static_cast(cos(Fixed64(+1))), std::cos(+1.0), 0.002); - EXPECT_NEAR(static_cast(cos(Fixed64(-1))), std::cos(-1.0), 0.002); - EXPECT_NEAR(static_cast(cos(Fixed64(+2))), std::cos(+2.0), 0.002); - EXPECT_NEAR(static_cast(cos(Fixed64(-2))), std::cos(-2.0), 0.002); - EXPECT_NEAR(static_cast(cos(Fixed64(+pi/2))), std::cos(+pi/2), 0.002); - EXPECT_NEAR(static_cast(cos(Fixed64(-pi/2))), std::cos(-pi/2), 0.002); - EXPECT_NEAR(static_cast(cos(Fixed64(+3))), std::cos(+3.0), 0.002); - EXPECT_NEAR(static_cast(cos(Fixed64(-3))), std::cos(-3.0), 0.002); - EXPECT_NEAR(static_cast(cos(Fixed64(+8))), std::cos(+8), 0.002); - EXPECT_NEAR(static_cast(cos(Fixed64(-8))), std::cos(-8), 0.002); - EXPECT_NEAR(static_cast(cos(Fixed64(+10))), std::cos(+10), 0.002); - EXPECT_NEAR(static_cast(cos(Fixed64(-10))), std::cos(-10), 0.002); -} -#endif - -TEST(Fixed32, atan) -{ - EXPECT_NEAR(static_cast(atan(Fixed32::GetInfinity())), - atan(std::numeric_limits::infinity()), 0.001); - EXPECT_NEAR(static_cast(atan(Fixed32::GetNegativeInfinity())), - atan(-std::numeric_limits::infinity()), 0.001); -} - -TEST(Fixed32, atan2_specials) -{ - EXPECT_TRUE(isnan(static_cast(atan2(Fixed32(0), Fixed32(0))))); - EXPECT_NEAR(static_cast(atan2(Fixed32(+1), Fixed32(0))), atan2(+1.0, 0.0), 0.01); - EXPECT_NEAR(static_cast(atan2(Fixed32(-1), Fixed32(0))), atan2(-1.0, 0.0), 0.01); - EXPECT_NEAR(static_cast(atan2(Fixed32(0), Fixed32(+1))), atan2(0.0, +1.0), 0.01); - EXPECT_NEAR(static_cast(atan2(Fixed32(0), Fixed32(-1))), atan2(0.0, -1.0), 0.01); - EXPECT_NEAR(static_cast(atan2(Fixed32(+1), Fixed32(+1))), atan2(+1.0, +1.0), 0.05); - EXPECT_NEAR(static_cast(atan2(Fixed32(+1), Fixed32(-1))), atan2(+1.0, -1.0), 0.05); - EXPECT_NEAR(static_cast(atan2(Fixed32(-1), Fixed32(+1))), atan2(-1.0, +1.0), 0.05); - EXPECT_NEAR(static_cast(atan2(Fixed32(-1), Fixed32(-1))), atan2(-1.0, -1.0), 0.05); -} - -TEST(Fixed32, atan2_angles) -{ - constexpr auto pi = double{3.14159265358979323846264338327950288}; - for (auto angleInDegs = -90; angleInDegs < +90; ++angleInDegs) - { - const auto angle = angleInDegs * pi / 180; - const auto s = sin(angle); - const auto c = cos(angle); - EXPECT_NEAR(static_cast(atan2(Fixed32(s), Fixed32(c))), angle, 0.05); - } -} - -TEST(Fixed32, Max) -{ - const auto max_internal_val = std::numeric_limits::max() - 1; - const auto max_fixed32 = *reinterpret_cast(&max_internal_val); - - EXPECT_EQ(Fixed32::GetMax(), Fixed32::GetMax()); - EXPECT_EQ(Fixed32::GetMax(), max_fixed32); - //EXPECT_EQ(static_cast(Fixed32::GetMax()), 131071.99993896484375000000L); - //std::cout << std::setprecision(22) << static_cast(Fixed32::GetMax()) << std::endl; - switch (Fixed32::FractionBits) - { - case 9: - EXPECT_NEAR(static_cast(Fixed32::GetMax()), 4.1943e+06, 4.0); - break; - case 14: - EXPECT_EQ(static_cast(Fixed32::GetMax()), 131071.9998779296875); - break; - } - - EXPECT_GT(Fixed32::GetMax(), Fixed32(0)); - EXPECT_GT(Fixed32::GetMax(), Fixed32::GetMin()); - EXPECT_GT(Fixed32::GetMax(), Fixed32::GetLowest()); - EXPECT_GT(Fixed32::GetMax(), Fixed32((1 << (31u - Fixed32::FractionBits)) - 1)); -} - -TEST(Fixed32, Min) -{ - EXPECT_EQ(Fixed32::GetMin(), Fixed32::GetMin()); - EXPECT_EQ(Fixed32::GetMin(), Fixed32(0, 1)); - switch (Fixed32::FractionBits) - { - case 9: - EXPECT_NEAR(static_cast(Fixed32::GetMin()), 0.00195312, 0.0000001); - break; - case 14: - EXPECT_EQ(static_cast(Fixed32::GetMin()), 0.00006103515625000000); - break; - } - - EXPECT_LT(Fixed32::GetMin(), Fixed32::GetMax()); - - EXPECT_GT(Fixed32::GetMin(), Fixed32(0)); - EXPECT_GT(Fixed32::GetMin(), Fixed32::GetLowest()); -} - -TEST(Fixed32, Lowest) -{ - const auto lowest_internal_val = std::numeric_limits::min() + 2; - const auto lowest_fixed32 = *reinterpret_cast(&lowest_internal_val); - - EXPECT_EQ(Fixed32::GetLowest(), Fixed32::GetLowest()); - EXPECT_EQ(Fixed32::GetLowest(), lowest_fixed32); - //EXPECT_EQ(static_cast(Fixed32::GetLowest()), -131072.00000000000000000000L); - //std::cout << std::setprecision(22) << static_cast(Fixed32::GetLowest()) << std::endl; - switch (Fixed32::FractionBits) - { - case 9: - EXPECT_NEAR(static_cast(Fixed32::GetLowest()), -4.1943e+06, 4.0); - break; - case 14: - EXPECT_EQ(static_cast(Fixed32::GetLowest()), -131071.9998779296875); - break; - } - EXPECT_LT(Fixed32::GetLowest(), Fixed32(0)); - EXPECT_LT(Fixed32::GetLowest(), Fixed32(-((1 << (31u - Fixed32::FractionBits)) - 1), 0u)); - EXPECT_LT(Fixed32::GetLowest(), Fixed32(-((1 << (31u - Fixed32::FractionBits)) - 1), (1u << Fixed32::FractionBits) - 1u)); - EXPECT_EQ(Fixed32::GetLowest(), -Fixed32::GetMax()); -} - -TEST(Fixed32, SubtractingFromLowestGetsNegativeInfinity) -{ - EXPECT_EQ(Fixed32::GetLowest() - Fixed32::GetMin(), Fixed32::GetNegativeInfinity()); - EXPECT_EQ(Fixed32::GetLowest() - 1, Fixed32::GetNegativeInfinity()); -} - -TEST(Fixed32, AddingToMaxGetsInfinity) -{ - EXPECT_EQ(Fixed32::GetMax() + Fixed32::GetMin(), Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetMax() + 1, Fixed32::GetInfinity()); -} - -TEST(Fixed32, MinusInfinityEqualsNegativeInfinity) -{ - EXPECT_EQ(-Fixed32::GetInfinity(), Fixed32::GetNegativeInfinity()); -} - -TEST(Fixed32, InfinityEqualsMinusNegativeInfinity) -{ - EXPECT_EQ(Fixed32::GetInfinity(), -Fixed32::GetNegativeInfinity()); -} - -TEST(Fixed32, InifnityTimesPositiveIsInfinity) -{ - EXPECT_EQ(Fixed32::GetInfinity() * 1, Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() * 2, Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() * 0.5, Fixed32::GetInfinity()); -} - -TEST(Fixed32, InifnityDividedByPositiveIsInfinity) -{ - EXPECT_EQ(Fixed32::GetInfinity() / 1, Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() / 2, Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() / 0.5, Fixed32::GetInfinity()); -} - -TEST(Fixed32, InfinityDividedByInfinityIsNaN) -{ - EXPECT_TRUE(isnan(Fixed32::GetInfinity() / Fixed32::GetInfinity())); -} - -TEST(Fixed32, InifnityTimesNegativeIsNegativeInfinity) -{ - EXPECT_EQ(Fixed32::GetInfinity() * -1, -Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() * -2, -Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() * -0.5, -Fixed32::GetInfinity()); -} - -TEST(Fixed32, InifnityDividedByNegativeIsNegativeInfinity) -{ - EXPECT_EQ(Fixed32::GetInfinity() / -1, -Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() / -2, -Fixed32::GetInfinity()); - EXPECT_EQ(Fixed32::GetInfinity() / -0.5, -Fixed32::GetInfinity()); -} - -TEST(Fixed32, InfinityMinusNegativeInfinityIsInfinity) -{ - EXPECT_EQ(Fixed32::GetInfinity() - -Fixed32::GetInfinity(), Fixed32::GetInfinity()); -} - -TEST(Fixed32, NegativeInfinityMinusInfinityIsNegativeInfinity) -{ - EXPECT_EQ(-Fixed32::GetInfinity() - Fixed32::GetInfinity(), -Fixed32::GetInfinity()); -} - -TEST(Fixed32, NaN) -{ - EXPECT_TRUE(isnan(Fixed32::GetNaN())); - EXPECT_TRUE(isnan(Fixed32::GetInfinity() / Fixed32::GetInfinity())); - EXPECT_TRUE(isnan(Fixed32::GetInfinity() - Fixed32::GetInfinity())); - EXPECT_TRUE(isnan(-Fixed32::GetInfinity() - -Fixed32::GetInfinity())); - EXPECT_TRUE(isnan(-Fixed32::GetInfinity() + Fixed32::GetInfinity())); - - EXPECT_FALSE(isnan(Fixed32{0})); - EXPECT_FALSE(isnan(Fixed32{10.0f})); - EXPECT_FALSE(isnan(Fixed32{-10.0f})); - EXPECT_FALSE(isnan(Fixed32::GetInfinity())); - EXPECT_FALSE(isnan(Fixed32::GetNegativeInfinity())); - EXPECT_FALSE(isnan(Fixed32::GetMax())); - EXPECT_FALSE(isnan(Fixed32::GetMin())); - EXPECT_FALSE(isnan(Fixed32::GetLowest())); -} - -TEST(Fixed32, InfinityTimesZeroIsNaN) -{ - EXPECT_TRUE(isnan(Fixed32::GetInfinity() * 0)); -} - -TEST(Fixed32, Comparators) -{ - EXPECT_FALSE(Fixed32::GetNaN() > 0.0f); - EXPECT_FALSE(Fixed32::GetNaN() < 0.0f); - EXPECT_FALSE(Fixed32::GetNaN() == 0.0f); - EXPECT_TRUE(Fixed32::GetNaN() != 0.0f); - EXPECT_FALSE(Fixed32::GetNaN() == Fixed32::GetNaN()); -} - -TEST(Fixed32, BiggerValsIdenticallyInaccurate) -{ - // Check that Real doesn't suffer from inconstent inaccuracy (like float has depending on - // the float's value). - auto last_delta = float(0); - auto val = Fixed32{1}; - const auto max = sizeof(Fixed32) * 8 - Fixed32::FractionBits - 1; - for (auto i = decltype(max){0}; i < max; ++i) - { - const auto next = nextafter(val, std::numeric_limits::max()); - const auto delta = static_cast(next - val); - ASSERT_EQ(val + (delta / 2.0f), val); -#if 0 - std::cout << std::hexfloat; - std::cout << "For " << std::setw(7) << val << ", delta of next value is " << std::setw(7) << delta; - std::cout << std::defaultfloat; - std::cout << ": ie. at " << std::setw(6) << val; - std::cout << std::fixed; - std::cout << ", delta is " << delta; - std::cout << std::endl; -#endif - val *= 2; - if (last_delta != 0) - { - ASSERT_EQ(delta, last_delta); - } - last_delta = delta; - } -} - -TEST(Fixed32, AdditionAssignment) -{ - Fixed32 foo; - foo = 0; - foo += Fixed32::GetNegativeInfinity(); - EXPECT_EQ(foo, -std::numeric_limits::infinity()); - foo = std::numeric_limits::lowest(); - foo += -1; - EXPECT_EQ(foo, Fixed32::GetNegativeInfinity()); -} - -TEST(Fixed32, SubtractionAssignment) -{ - Fixed32 foo; - foo = 0; - foo -= 0; - EXPECT_EQ(foo, Fixed32{0}); - foo = 0; - foo -= 1; - EXPECT_EQ(foo, Fixed32{-1}); - foo = std::numeric_limits::max(); - foo -= Fixed32{-2}; - EXPECT_EQ(foo, Fixed32::GetInfinity()); -} - -TEST(Fixed32, MultiplicationAssignment) -{ - Fixed32 foo; - foo = Fixed32::GetNaN(); - foo *= Fixed32{0}; - EXPECT_TRUE(foo.isnan()); - foo = 0; - foo *= Fixed32::GetNaN(); - EXPECT_TRUE(foo.isnan()); - foo = std::numeric_limits::min(); - foo *= std::numeric_limits::min(); - EXPECT_EQ(foo, Fixed32(0)); - foo = std::numeric_limits::lowest(); - foo *= 2; - EXPECT_EQ(foo, Fixed32::GetNegativeInfinity()); -} - -TEST(Fixed32, DivisionAssignment) -{ - Fixed32 foo; - foo = Fixed32::GetNaN(); - foo /= Fixed32{1}; - EXPECT_TRUE(foo.isnan()); - foo = 0; - foo /= Fixed32::GetNaN(); - EXPECT_TRUE(foo.isnan()); - foo = 1; - foo /= Fixed32::GetInfinity(); - EXPECT_EQ(foo, Fixed32(0)); - foo = std::numeric_limits::max(); - ASSERT_EQ(foo, std::numeric_limits::max()); - foo /= Fixed32(0.5f); - EXPECT_EQ(foo, Fixed32::GetInfinity()); - foo = std::numeric_limits::lowest(); - ASSERT_TRUE(foo.isfinite()); - foo /= Fixed32(0.5); - EXPECT_EQ(foo, Fixed32::GetNegativeInfinity()); -} - -TEST(Fixed32, GetSign) -{ - Fixed32 foo; - foo = 0; - EXPECT_GT(foo.getsign(), 0); - foo = Fixed32(-32.412); - EXPECT_LT(foo.getsign(), 0); -} - -TEST(Fixed32, StreamOut) -{ - std::ostringstream os; - os << Fixed32(2.2f); - EXPECT_STREQ(os.str().c_str(), "2.19922"); -} - -#ifdef PLAYRHO_INT128 -TEST(Fixed64, StreamOut) -{ - std::ostringstream os; - os << Fixed64(2.2f); - EXPECT_STREQ(os.str().c_str(), "2.2"); -} -#endif - -TEST(Fixed, Int32TypeAnd0bits) -{ - using fixed = Fixed; - - const auto zero = fixed(0); - EXPECT_TRUE(zero == zero); - EXPECT_EQ(zero, zero); - - const auto one = fixed(1); - EXPECT_TRUE(one == one); - EXPECT_EQ(one, one); - - EXPECT_NE(one, zero); - EXPECT_NE(zero, one); - EXPECT_GT(one, zero); - EXPECT_GE(one, zero); - EXPECT_GE(one, one); - EXPECT_LT(zero, one); - EXPECT_LE(zero, one); - - const auto two = one + one; - EXPECT_NE(one, two); - EXPECT_GT(two, one); - EXPECT_GT(two, zero); - - EXPECT_EQ(one * one, one); - EXPECT_EQ(one * two, two); - EXPECT_EQ(two / two, one); - EXPECT_EQ(two - two, zero); -} - -TEST(Fixed, LessThan) -{ - using fixed_32_0 = Fixed; - EXPECT_LT(fixed_32_0(0), fixed_32_0(1)); -} - -TEST(Fixed, nextafter) -{ - using fixed_32_0 = Fixed; - EXPECT_EQ(static_cast(nextafter(fixed_32_0(0), fixed_32_0( 0))), 0.0); - EXPECT_EQ(static_cast(nextafter(fixed_32_0(0), fixed_32_0(+1))), +1.0); - EXPECT_EQ(static_cast(nextafter(fixed_32_0(0), fixed_32_0(-1))), -1.0); - - using fixed_32_1 = Fixed; - EXPECT_EQ(static_cast(nextafter(fixed_32_1(0), fixed_32_1( 0))), 0.0); - EXPECT_EQ(static_cast(nextafter(fixed_32_1(0), fixed_32_1(+1))), +0.5); - EXPECT_EQ(static_cast(nextafter(fixed_32_1(0), fixed_32_1(-1))), -0.5); - - using fixed_32_2 = Fixed; - EXPECT_EQ(static_cast(nextafter(fixed_32_2(0), fixed_32_2( 0))), 0.0); - EXPECT_EQ(static_cast(nextafter(fixed_32_2(0), fixed_32_2(+1))), +0.25); - EXPECT_EQ(static_cast(nextafter(fixed_32_2(0), fixed_32_2(-1))), -0.25); -} diff --git a/UnitTests/Real.cpp b/UnitTests/Real.cpp index dd766da6ea..57736cdb5a 100644 --- a/UnitTests/Real.cpp +++ b/UnitTests/Real.cpp @@ -28,14 +28,6 @@ using namespace playrho; -TEST(Real, ByteSizeIs_4_8_or_16) -{ - // Check size at test runtime instead of compile-time via static_assert to avoid stopping - // builds and to report actual size rather than just reporting that expected size is wrong. - const auto size = sizeof(Real); - EXPECT_TRUE(size == std::size_t(4) || size == std::size_t(8) || size == std::size_t(16)); -} - TEST(Real, GetTypeName) { const auto name = std::string(GetTypeName()); @@ -58,16 +50,6 @@ TEST(Real, GetTypeName) is_expected = true; break; } - if (name == "Fixed32") - { - is_expected = true; - break; - } - if (name == "Fixed64") - { - is_expected = true; - break; - } } while (false); EXPECT_TRUE(is_expected);