From b8741eaf6f90ee3b332e36e9e9d958645e916ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D1=80=D0=B0=D0=BD=D0=B8=D0=BC=D0=B8=D1=80=20=D0=9A?= =?UTF-8?q?=D0=B0=D1=80=D0=B0=D1=9F=D0=B8=D1=9B?= Date: Mon, 7 Oct 2024 18:57:57 -0700 Subject: [PATCH] Added rcpSafe. --- include/bx/inline/math.inl | 43 +++++++++++---- include/bx/math.h | 14 +++++ tests/math_test.cpp | 107 ++++++++++++++++++++++--------------- 3 files changed, 113 insertions(+), 51 deletions(-) diff --git a/include/bx/inline/math.inl b/include/bx/inline/math.inl index 826b8f9..049a355 100644 --- a/include/bx/inline/math.inl +++ b/include/bx/inline/math.inl @@ -411,7 +411,7 @@ namespace bx inline BX_CONST_FUNC float rsqrtRef(float _a) { - if (_a < kNearZero) + if (_a < kFloatSmallest) { return kFloatInfinity; } @@ -421,7 +421,7 @@ namespace bx inline BX_CONST_FUNC float rsqrtSimd(float _a) { - if (_a < kNearZero) + if (_a < kFloatSmallest) { return kFloatInfinity; } @@ -441,7 +441,7 @@ namespace bx inline BX_CONST_FUNC float sqrtRef(float _a) { - if (_a < 0.0F) + if (_a < 0.0f) { return bitsToFloat(kFloatExponentMask | kFloatMantissaMask); } @@ -451,11 +451,11 @@ namespace bx inline BX_CONST_FUNC float sqrtSimd(float _a) { - if (_a < 0.0F) + if (_a < 0.0f) { return bitsToFloat(kFloatExponentMask | kFloatMantissaMask); } - else if (_a < kNearZero) + else if (_a < kFloatSmallest) { return 0.0f; } @@ -527,9 +527,14 @@ namespace bx return 1.0f / _a; } + inline BX_CONSTEXPR_FUNC float rcpSafe(float _a) + { + return rcp(copySign(max(kFloatSmallest, abs(_a) ), _a) ); + } + inline BX_CONSTEXPR_FUNC float mod(float _a, float _b) { - return _a - _b * floor(_a / _b); + return _a - _b * floor(mul(_a, rcp(_b) ) ); } inline BX_CONSTEXPR_FUNC bool isEqual(float _a, float _b, float _epsilon) @@ -803,11 +808,21 @@ namespace bx return mul(_a, rcp(_b) ); } + inline BX_CONSTEXPR_FUNC Vec3 divSafe(const Vec3 _a, const Vec3 _b) + { + return mul(_a, rcpSafe(_b) ); + } + inline BX_CONSTEXPR_FUNC Vec3 div(const Vec3 _a, float _b) { return mul(_a, rcp(_b) ); } + inline BX_CONSTEXPR_FUNC Vec3 divSafe(const Vec3 _a, float _b) + { + return mul(_a, rcpSafe(_b) ); + } + inline BX_CONSTEXPR_FUNC Vec3 nms(const Vec3 _a, const float _b, const Vec3 _c) { return sub(_c, mul(_a, _b) ); @@ -910,9 +925,19 @@ namespace bx { return { - 1.0f / _a.x, - 1.0f / _a.y, - 1.0f / _a.z, + rcp(_a.x), + rcp(_a.y), + rcp(_a.z), + }; + } + + inline BX_CONSTEXPR_FUNC Vec3 rcpSafe(const Vec3 _a) + { + return + { + rcpSafe(_a.x), + rcpSafe(_a.y), + rcpSafe(_a.z), }; } diff --git a/include/bx/math.h b/include/bx/math.h index a7b5e2b..27d24e5 100644 --- a/include/bx/math.h +++ b/include/bx/math.h @@ -355,6 +355,10 @@ namespace bx /// BX_CONSTEXPR_FUNC float rcp(float _a); + /// Returns reciprocal of _a. + /// + BX_CONSTEXPR_FUNC float rcpSafe(float _a); + /// Returns the floating-point remainder of the division operation _a/_b. /// BX_CONSTEXPR_FUNC float mod(float _a, float _b); @@ -431,9 +435,15 @@ namespace bx /// BX_CONSTEXPR_FUNC Vec3 div(const Vec3 _a, const Vec3 _b); + /// + BX_CONSTEXPR_FUNC Vec3 divSafe(const Vec3 _a, const Vec3 _b); + /// BX_CONSTEXPR_FUNC Vec3 div(const Vec3 _a, float _b); + /// + BX_CONSTEXPR_FUNC Vec3 divSafe(const Vec3 _a, float _b); + /// Returns result of negated multiply-sub operation -(_a * _b - _c) -> _c - _a * _b. /// BX_CONSTEXPR_FUNC Vec3 nms(const Vec3 _a, const float _b, const Vec3 _c); @@ -482,6 +492,10 @@ namespace bx /// BX_CONSTEXPR_FUNC Vec3 rcp(const Vec3 _a); + /// Returns component wise reciprocal of _a. + /// + BX_CONSTEXPR_FUNC Vec3 rcpSafe(const Vec3 _a); + /// BX_CONSTEXPR_FUNC bool isEqual(const Vec3 _a, const Vec3 _b, float _epsilon); diff --git a/tests/math_test.cpp b/tests/math_test.cpp index a1d6235..2dedc43 100644 --- a/tests/math_test.cpp +++ b/tests/math_test.cpp @@ -22,9 +22,9 @@ TEST_CASE("isFinite, isInfinite, isNan", "[math]") REQUIRE(::__isfinitef(u.f) == bx::isFinite(u.f) ); REQUIRE(::__isinff(u.f) == bx::isInfinite(u.f) ); #elif BX_COMPILER_MSVC - REQUIRE(!!::isnan(u.f) == bx::isNan(u.f)); - REQUIRE(!!::isfinite(u.f) == bx::isFinite(u.f)); - REQUIRE(!!::isinf(u.f) == bx::isInfinite(u.f)); + REQUIRE(!!::isnan(u.f) == bx::isNan(u.f) ); + REQUIRE(!!::isfinite(u.f) == bx::isFinite(u.f) ); + REQUIRE(!!::isinf(u.f) == bx::isInfinite(u.f) ); #else REQUIRE(::isnanf(u.f) == bx::isNan(u.f) ); REQUIRE(::finitef(u.f) == bx::isFinite(u.f) ); @@ -148,7 +148,7 @@ TEST_CASE("floorLog2", "[math]") } } -TEST_CASE("ceil/floorLog2", "[math]") +TEST_CASE("ceilLog2 & floorLog2", "[math]") { { uint32_t prev = 0; @@ -234,40 +234,56 @@ TEST_CASE("findFirstSet", "[math]") BX_PRAGMA_DIAGNOSTIC_PUSH(); BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4723) // potential divide by 0 +TEST_CASE("rcp", "[math][libm]") +{ + REQUIRE(1.0f == bx::rcp(1.0f) ); + REQUIRE(2.0f == bx::rcp(0.5f) ); + REQUIRE(bx::isInfinite(bx::rcp( 0.0f) ) ); + REQUIRE(bx::isInfinite(bx::rcp(-0.0f) ) ); +} + +TEST_CASE("rcpSafe", "[math][libm]") +{ + REQUIRE(1.0f == bx::rcpSafe(1.0f) ); + REQUIRE(2.0f == bx::rcpSafe(0.5f) ); + REQUIRE(!bx::isInfinite(bx::rcpSafe( 0.0f) ) ); + REQUIRE(!bx::isInfinite(bx::rcpSafe(-0.0f) ) ); +} + TEST_CASE("rsqrt", "[math][libm]") { bx::WriterI* writer = bx::getNullOut(); bx::Error err; // rsqrtRef - REQUIRE(bx::isInfinite(bx::rsqrtRef(0.0f))); + REQUIRE(bx::isInfinite(bx::rsqrtRef(0.0f) ) ); for (float xx = bx::kNearZero; xx < 100.0f; xx += 0.1f) { - bx::write(writer, &err, "rsqrtRef(%f) == %f (expected: %f)\n", xx, bx::rsqrtRef(xx), 1.0f / ::sqrtf(xx)); - REQUIRE(err.isOk()); - REQUIRE(bx::isEqual(bx::rsqrtRef(xx), 1.0f / ::sqrtf(xx), 0.00001f)); + bx::write(writer, &err, "rsqrtRef(%f) == %f (expected: %f)\n", xx, bx::rsqrtRef(xx), 1.0f / ::sqrtf(xx) ); + REQUIRE(err.isOk() ); + REQUIRE(bx::isEqual(bx::rsqrtRef(xx), 1.0f / ::sqrtf(xx), 0.00001f) ); } // rsqrtSimd - REQUIRE(bx::isInfinite(bx::rsqrtSimd(0.0f))); + REQUIRE(bx::isInfinite(bx::rsqrtSimd(0.0f) ) ); for (float xx = bx::kNearZero; xx < 100.0f; xx += 0.1f) { - bx::write(writer, &err, "rsqrtSimd(%f) == %f (expected: %f)\n", xx, bx::rsqrtSimd(xx), 1.0f / ::sqrtf(xx)); - REQUIRE(err.isOk()); - REQUIRE(bx::isEqual(bx::rsqrtSimd(xx), 1.0f / ::sqrtf(xx), 0.00001f)); + bx::write(writer, &err, "rsqrtSimd(%f) == %f (expected: %f)\n", xx, bx::rsqrtSimd(xx), 1.0f / ::sqrtf(xx) ); + REQUIRE(err.isOk() ); + REQUIRE(bx::isEqual(bx::rsqrtSimd(xx), 1.0f / ::sqrtf(xx), 0.00001f) ); } // rsqrt - REQUIRE(bx::isInfinite(1.0f / ::sqrtf(0.0f))); - REQUIRE(bx::isInfinite(bx::rsqrt(0.0f))); + REQUIRE(bx::isInfinite(1.0f / ::sqrtf(0.0f) ) ); + REQUIRE(bx::isInfinite(bx::rsqrt(0.0f) ) ); for (float xx = bx::kNearZero; xx < 100.0f; xx += 0.1f) { - bx::write(writer, &err, "rsqrt(%f) == %f (expected: %f)\n", xx, bx::rsqrt(xx), 1.0f / ::sqrtf(xx)); - REQUIRE(err.isOk()); - REQUIRE(bx::isEqual(bx::rsqrt(xx), 1.0f / ::sqrtf(xx), 0.00001f)); + bx::write(writer, &err, "rsqrt(%f) == %f (expected: %f)\n", xx, bx::rsqrt(xx), 1.0f / ::sqrtf(xx) ); + REQUIRE(err.isOk() ); + REQUIRE(bx::isEqual(bx::rsqrt(xx), 1.0f / ::sqrtf(xx), 0.00001f) ); } } @@ -277,54 +293,54 @@ TEST_CASE("sqrt", "[math][libm]") bx::Error err; // sqrtRef - REQUIRE(bx::isNan(bx::sqrtRef(-1.0f))); - REQUIRE(bx::isEqual(bx::sqrtRef(0.0f), ::sqrtf(0.0f), 0.0f)); - REQUIRE(bx::isEqual(bx::sqrtRef(1.0f), ::sqrtf(1.0f), 0.0f)); + REQUIRE(bx::isNan(bx::sqrtRef(-1.0f) ) ); + REQUIRE(bx::isEqual(bx::sqrtRef(0.0f), ::sqrtf(0.0f), 0.0f) ); + REQUIRE(bx::isEqual(bx::sqrtRef(1.0f), ::sqrtf(1.0f), 0.0f) ); for (float xx = 0.0f; xx < 1000000.0f; xx += 1000.f) { - bx::write(writer, &err, "sqrtRef(%f) == %f (expected: %f)\n", xx, bx::sqrtRef(xx), ::sqrtf(xx)); - REQUIRE(err.isOk()); - REQUIRE(bx::isEqual(bx::sqrtRef(xx), ::sqrtf(xx), 0.00001f)); + bx::write(writer, &err, "sqrtRef(%f) == %f (expected: %f)\n", xx, bx::sqrtRef(xx), ::sqrtf(xx) ); + REQUIRE(err.isOk() ); + REQUIRE(bx::isEqual(bx::sqrtRef(xx), ::sqrtf(xx), 0.00001f) ); } // sqrtSimd - REQUIRE(bx::isNan(bx::sqrtSimd(-1.0f))); - REQUIRE(bx::isEqual(bx::sqrtSimd(0.0f), ::sqrtf(0.0f), 0.0f)); - REQUIRE(bx::isEqual(bx::sqrtSimd(1.0f), ::sqrtf(1.0f), 0.0f)); + REQUIRE(bx::isNan(bx::sqrtSimd(-1.0f) ) ); + REQUIRE(bx::isEqual(bx::sqrtSimd(0.0f), ::sqrtf(0.0f), 0.0f) ); + REQUIRE(bx::isEqual(bx::sqrtSimd(1.0f), ::sqrtf(1.0f), 0.0f) ); for (float xx = 0.0f; xx < 1000000.0f; xx += 1000.f) { - bx::write(writer, &err, "sqrtSimd(%f) == %f (expected: %f)\n", xx, bx::sqrtSimd(xx), ::sqrtf(xx)); - REQUIRE(err.isOk()); - REQUIRE(bx::isEqual(bx::sqrtSimd(xx), ::sqrtf(xx), 0.00001f)); + bx::write(writer, &err, "sqrtSimd(%f) == %f (expected: %f)\n", xx, bx::sqrtSimd(xx), ::sqrtf(xx) ); + REQUIRE(err.isOk() ); + REQUIRE(bx::isEqual(bx::sqrtSimd(xx), ::sqrtf(xx), 0.00001f) ); } for (float xx = 0.0f; xx < 100.0f; xx += 0.1f) { - bx::write(writer, &err, "sqrt(%f) == %f (expected: %f)\n", xx, bx::sqrt(xx), ::sqrtf(xx)); - REQUIRE(err.isOk()); - REQUIRE(bx::isEqual(bx::sqrt(xx), ::sqrtf(xx), 0.00001f)); + bx::write(writer, &err, "sqrt(%f) == %f (expected: %f)\n", xx, bx::sqrt(xx), ::sqrtf(xx) ); + REQUIRE(err.isOk() ); + REQUIRE(bx::isEqual(bx::sqrt(xx), ::sqrtf(xx), 0.00001f) ); } // sqrt - REQUIRE(bx::isNan(::sqrtf(-1.0f))); - REQUIRE(bx::isNan(bx::sqrt(-1.0f))); - REQUIRE(bx::isEqual(bx::sqrt(0.0f), ::sqrtf(0.0f), 0.0f)); - REQUIRE(bx::isEqual(bx::sqrt(1.0f), ::sqrtf(1.0f), 0.0f)); + REQUIRE(bx::isNan(::sqrtf(-1.0f) ) ); + REQUIRE(bx::isNan(bx::sqrt(-1.0f) ) ); + REQUIRE(bx::isEqual(bx::sqrt(0.0f), ::sqrtf(0.0f), 0.0f) ); + REQUIRE(bx::isEqual(bx::sqrt(1.0f), ::sqrtf(1.0f), 0.0f) ); for (float xx = 0.0f; xx < 1000000.0f; xx += 1000.f) { - bx::write(writer, &err, "sqrt(%f) == %f (expected: %f)\n", xx, bx::sqrt(xx), ::sqrtf(xx)); - REQUIRE(err.isOk()); - REQUIRE(bx::isEqual(bx::sqrt(xx), ::sqrtf(xx), 0.00001f)); + bx::write(writer, &err, "sqrt(%f) == %f (expected: %f)\n", xx, bx::sqrt(xx), ::sqrtf(xx) ); + REQUIRE(err.isOk() ); + REQUIRE(bx::isEqual(bx::sqrt(xx), ::sqrtf(xx), 0.00001f) ); } for (float xx = 0.0f; xx < 100.0f; xx += 0.1f) { - bx::write(writer, &err, "sqrt(%f) == %f (expected: %f)\n", xx, bx::sqrt(xx), ::sqrtf(xx)); - REQUIRE(err.isOk()); - REQUIRE(bx::isEqual(bx::sqrt(xx), ::sqrtf(xx), 0.00001f)); + bx::write(writer, &err, "sqrt(%f) == %f (expected: %f)\n", xx, bx::sqrt(xx), ::sqrtf(xx) ); + REQUIRE(err.isOk() ); + REQUIRE(bx::isEqual(bx::sqrt(xx), ::sqrtf(xx), 0.00001f) ); } } @@ -341,6 +357,10 @@ TEST_CASE("abs", "[math][libm]") TEST_CASE("mod", "[math][libm]") { REQUIRE(389.0f == bx::mod(1389.0f, 1000.0f) ); + REQUIRE( 89.0f == bx::mod(1389.0f, 100.0f) ); + REQUIRE( 9.0f == bx::mod(1389.0f, 10.0f) ); + REQUIRE( 4.0f == bx::mod(1389.0f, 5.0f) ); + REQUIRE( 1.0f == bx::mod(1389.0f, 2.0f) ); } TEST_CASE("floor", "[math][libm]") @@ -760,4 +780,7 @@ TEST_CASE("limits", "[math]") STATIC_REQUIRE(bx::LimitsT::min == std::numeric_limits::lowest() ); STATIC_REQUIRE(bx::LimitsT::max == std::numeric_limits::max() ); + + STATIC_REQUIRE(bx::kFloatSmallest == std::numeric_limits::min() ); + STATIC_REQUIRE(bx::kDoubleSmallest == std::numeric_limits::min() ); }