diff --git a/include/bx/inline/math.inl b/include/bx/inline/math.inl index f33b873..0d19de2 100644 --- a/include/bx/inline/math.inl +++ b/include/bx/inline/math.inl @@ -371,16 +371,24 @@ namespace bx } template<> inline BX_CONSTEXPR_FUNC uint8_t countTrailingZeros(uint8_t _val) { return bx::min(8, countTrailingZeros(_val) ); } - template<> inline BX_CONSTEXPR_FUNC uint8_t countTrailingZeros(int8_t _val) { return countTrailingZeros(_val); } + template<> inline BX_CONSTEXPR_FUNC uint8_t countTrailingZeros(int8_t _val) { return countTrailingZeros(_val); } template<> inline BX_CONSTEXPR_FUNC uint8_t countTrailingZeros(uint16_t _val) { return bx::min(16, countTrailingZeros(_val) ); } - template<> inline BX_CONSTEXPR_FUNC uint8_t countTrailingZeros(int16_t _val) { return countTrailingZeros(_val); } - template<> inline BX_CONSTEXPR_FUNC uint8_t countTrailingZeros(int32_t _val) { return countTrailingZeros(_val); } - template<> inline BX_CONSTEXPR_FUNC uint8_t countTrailingZeros(int64_t _val) { return countTrailingZeros(_val); } + template<> inline BX_CONSTEXPR_FUNC uint8_t countTrailingZeros(int16_t _val) { return countTrailingZeros(_val); } + template<> inline BX_CONSTEXPR_FUNC uint8_t countTrailingZeros(int32_t _val) { return countTrailingZeros(_val); } + template<> inline BX_CONSTEXPR_FUNC uint8_t countTrailingZeros(int64_t _val) { return countTrailingZeros(_val); } template - inline BX_CONSTEXPR_FUNC uint8_t findFirstSet(Ty _x) + inline BX_CONSTEXPR_FUNC uint8_t findFirstSet(Ty _val) { - return Ty(0) == _x ? uint8_t(0) : countTrailingZeros(_x) + 1; + BX_STATIC_ASSERT(isInteger(), "Type Ty must be of integer type!"); + return Ty(0) == _val ? uint8_t(0) : countTrailingZeros(_val) + 1; + } + + template + inline BX_CONSTEXPR_FUNC uint8_t findLastSet(Ty _val) + { + BX_STATIC_ASSERT(isInteger(), "Type Ty must be of integer type!"); + return Ty(0) == _val ? uint8_t(0) : sizeof(Ty)*8 - countLeadingZeros(_val); } template diff --git a/include/bx/math.h b/include/bx/math.h index 715d0bd..21cf660 100644 --- a/include/bx/math.h +++ b/include/bx/math.h @@ -299,6 +299,11 @@ namespace bx template BX_CONSTEXPR_FUNC uint8_t findFirstSet(Ty _val); + /// Find last set. + /// + template + BX_CONSTEXPR_FUNC uint8_t findLastSet(Ty _val); + /// Returns the next smallest integer base 2 logarithm of _a. /// template diff --git a/tests/math_test.cpp b/tests/math_test.cpp index 3921f5f..be48040 100644 --- a/tests/math_test.cpp +++ b/tests/math_test.cpp @@ -56,17 +56,17 @@ TEST_CASE("log2", "[math][libm]") TEST_CASE("ceilLog2", "[math]") { - REQUIRE(0 == bx::ceilLog2(-1) ); - REQUIRE(0 == bx::ceilLog2(0) ); - REQUIRE(0 == bx::ceilLog2(1) ); - REQUIRE(1 == bx::ceilLog2(2) ); - REQUIRE(2 == bx::ceilLog2(4) ); - REQUIRE(3 == bx::ceilLog2(8) ); - REQUIRE(4 == bx::ceilLog2(16) ); - REQUIRE(5 == bx::ceilLog2(32) ); - REQUIRE(6 == bx::ceilLog2(64) ); - REQUIRE(7 == bx::ceilLog2(128) ); - REQUIRE(8 == bx::ceilLog2(256) ); + STATIC_REQUIRE(0 == bx::ceilLog2(-1) ); + STATIC_REQUIRE(0 == bx::ceilLog2(0) ); + STATIC_REQUIRE(0 == bx::ceilLog2(1) ); + STATIC_REQUIRE(1 == bx::ceilLog2(2) ); + STATIC_REQUIRE(2 == bx::ceilLog2(4) ); + STATIC_REQUIRE(3 == bx::ceilLog2(8) ); + STATIC_REQUIRE(4 == bx::ceilLog2(16) ); + STATIC_REQUIRE(5 == bx::ceilLog2(32) ); + STATIC_REQUIRE(6 == bx::ceilLog2(64) ); + STATIC_REQUIRE(7 == bx::ceilLog2(128) ); + STATIC_REQUIRE(8 == bx::ceilLog2(256) ); { uint32_t ii = 0; @@ -99,24 +99,23 @@ TEST_CASE("ceilLog2", "[math]") for (uint32_t ii = 1; ii < INT32_MAX; ii += rand()%(1<<13)+1) { -// DBG("%u: %u %u", ii, bx::uint32_nextpow2(ii), bx::nextPow2(ii) ); REQUIRE(bx::nextPow2(ii) == bx::uint32_nextpow2(ii) ); } } TEST_CASE("floorLog2", "[math]") { - REQUIRE(0 == bx::floorLog2(-1) ); - REQUIRE(0 == bx::floorLog2(0) ); - REQUIRE(0 == bx::floorLog2(1) ); - REQUIRE(1 == bx::floorLog2(2) ); - REQUIRE(2 == bx::floorLog2(4) ); - REQUIRE(3 == bx::floorLog2(8) ); - REQUIRE(4 == bx::floorLog2(16) ); - REQUIRE(5 == bx::floorLog2(32) ); - REQUIRE(6 == bx::floorLog2(64) ); - REQUIRE(7 == bx::floorLog2(128) ); - REQUIRE(8 == bx::floorLog2(256) ); + STATIC_REQUIRE(0 == bx::floorLog2(-1) ); + STATIC_REQUIRE(0 == bx::floorLog2(0) ); + STATIC_REQUIRE(0 == bx::floorLog2(1) ); + STATIC_REQUIRE(1 == bx::floorLog2(2) ); + STATIC_REQUIRE(2 == bx::floorLog2(4) ); + STATIC_REQUIRE(3 == bx::floorLog2(8) ); + STATIC_REQUIRE(4 == bx::floorLog2(16) ); + STATIC_REQUIRE(5 == bx::floorLog2(32) ); + STATIC_REQUIRE(6 == bx::floorLog2(64) ); + STATIC_REQUIRE(7 == bx::floorLog2(128) ); + STATIC_REQUIRE(8 == bx::floorLog2(256) ); { uint32_t ii = 0; @@ -172,63 +171,132 @@ TEST_CASE("ceilLog2 & floorLog2", "[math]") TEST_CASE("countTrailingZeros", "[math]") { - REQUIRE( 0 == bx::countTrailingZeros(1) ); - REQUIRE( 7 == bx::countTrailingZeros(1<<7) ); - REQUIRE( 8 == bx::countTrailingZeros(0) ); - REQUIRE( 1 == bx::countTrailingZeros(0x3e) ); - REQUIRE( 0 == bx::countTrailingZeros(1) ); - REQUIRE(15 == bx::countTrailingZeros(1<<15) ); - REQUIRE(16 == bx::countTrailingZeros(0) ); - REQUIRE( 0 == bx::countTrailingZeros(1) ); - REQUIRE(32 == bx::countTrailingZeros(0) ); - REQUIRE(31 == bx::countTrailingZeros(1u<<31) ); - REQUIRE( 0 == bx::countTrailingZeros(1) ); - REQUIRE(64 == bx::countTrailingZeros(0) ); + STATIC_REQUIRE( 0 == bx::countTrailingZeros(1) ); + STATIC_REQUIRE( 7 == bx::countTrailingZeros(1<<7) ); + STATIC_REQUIRE( 8 == bx::countTrailingZeros(0) ); + STATIC_REQUIRE( 1 == bx::countTrailingZeros(0x3e) ); + STATIC_REQUIRE( 0 == bx::countTrailingZeros(1) ); + STATIC_REQUIRE(15 == bx::countTrailingZeros(1<<15) ); + STATIC_REQUIRE(16 == bx::countTrailingZeros(0) ); + STATIC_REQUIRE( 0 == bx::countTrailingZeros(1) ); + STATIC_REQUIRE(32 == bx::countTrailingZeros(0) ); + STATIC_REQUIRE(31 == bx::countTrailingZeros(1u<<31) ); + STATIC_REQUIRE( 0 == bx::countTrailingZeros(1) ); + STATIC_REQUIRE(64 == bx::countTrailingZeros(0) ); } TEST_CASE("countLeadingZeros", "[math]") { - REQUIRE( 7 == bx::countLeadingZeros(1) ); - REQUIRE( 8 == bx::countLeadingZeros(0) ); - REQUIRE( 2 == bx::countLeadingZeros(0x3e) ); - REQUIRE(15 == bx::countLeadingZeros(1) ); - REQUIRE(16 == bx::countLeadingZeros(0) ); - REQUIRE(31 == bx::countLeadingZeros(1) ); - REQUIRE(32 == bx::countLeadingZeros(0) ); - REQUIRE(63 == bx::countLeadingZeros(1) ); - REQUIRE(64 == bx::countLeadingZeros(0) ); + STATIC_REQUIRE( 7 == bx::countLeadingZeros(1) ); + STATIC_REQUIRE( 8 == bx::countLeadingZeros(0) ); + STATIC_REQUIRE( 2 == bx::countLeadingZeros(0x3e) ); + STATIC_REQUIRE(15 == bx::countLeadingZeros(1) ); + STATIC_REQUIRE(16 == bx::countLeadingZeros(0) ); + STATIC_REQUIRE(31 == bx::countLeadingZeros(1) ); + STATIC_REQUIRE(32 == bx::countLeadingZeros(0) ); + STATIC_REQUIRE(63 == bx::countLeadingZeros(1) ); + STATIC_REQUIRE(64 == bx::countLeadingZeros(0) ); } TEST_CASE("countBits", "[math]") { - REQUIRE( 0 == bx::countBits(0) ); - REQUIRE( 1 == bx::countBits(1) ); + STATIC_REQUIRE( 0 == bx::countBits(0) ); + STATIC_REQUIRE( 1 == bx::countBits(1) ); - REQUIRE( 4 == bx::countBits(0x55) ); - REQUIRE( 8 == bx::countBits(0x5555) ); - REQUIRE(16 == bx::countBits(0x55555555) ); - REQUIRE(32 == bx::countBits(0x5555555555555555) ); + STATIC_REQUIRE( 4 == bx::countBits(0x55) ); + STATIC_REQUIRE( 8 == bx::countBits(0x5555) ); + STATIC_REQUIRE(16 == bx::countBits(0x55555555) ); + STATIC_REQUIRE(32 == bx::countBits(0x5555555555555555) ); - REQUIRE( 8 == bx::countBits(UINT8_MAX) ); - REQUIRE(16 == bx::countBits(UINT16_MAX) ); - REQUIRE(32 == bx::countBits(UINT32_MAX) ); - REQUIRE(64 == bx::countBits(UINT64_MAX) ); + STATIC_REQUIRE( 8 == bx::countBits(UINT8_MAX) ); + STATIC_REQUIRE(16 == bx::countBits(UINT16_MAX) ); + STATIC_REQUIRE(32 == bx::countBits(UINT32_MAX) ); + STATIC_REQUIRE(64 == bx::countBits(UINT64_MAX) ); +} + +template +void testFindFirstSet() +{ + for (uint8_t ii = 0, num = sizeof(Ty)*8; ii < num; ++ii) + { + { + const Ty val = Ty(1) << ii; + const uint8_t result = bx::findFirstSet(val); + REQUIRE(result == ii + 1); + } + + { + const Ty val = ( (Ty(1) << ii) ) | (Ty(1) << (num - 1) ); + const uint8_t result = bx::findFirstSet(val); + REQUIRE(result == ii + 1); + } + } } TEST_CASE("findFirstSet", "[math]") { - REQUIRE( 1 == bx::findFirstSet(1) ); - REQUIRE( 8 == bx::findFirstSet(1<<7) ); - REQUIRE( 0 == bx::findFirstSet(0) ); - REQUIRE( 2 == bx::findFirstSet(0x3e) ); - REQUIRE( 1 == bx::findFirstSet(1) ); - REQUIRE(16 == bx::findFirstSet(1<<15) ); - REQUIRE( 0 == bx::findFirstSet(0) ); - REQUIRE( 1 == bx::findFirstSet(1) ); - REQUIRE( 0 == bx::findFirstSet(0) ); - REQUIRE(32 == bx::findFirstSet(1u<<31) ); - REQUIRE( 1 == bx::findFirstSet(1) ); - REQUIRE( 0 == bx::findFirstSet(0) ); + STATIC_REQUIRE( 1 == bx::findFirstSet(1) ); + STATIC_REQUIRE( 8 == bx::findFirstSet(1<<7) ); + STATIC_REQUIRE( 0 == bx::findFirstSet(0) ); + STATIC_REQUIRE( 2 == bx::findFirstSet(0x3e) ); + STATIC_REQUIRE( 1 == bx::findFirstSet(1) ); + STATIC_REQUIRE(16 == bx::findFirstSet(1<<15) ); + STATIC_REQUIRE( 0 == bx::findFirstSet(0) ); + STATIC_REQUIRE( 1 == bx::findFirstSet(1) ); + STATIC_REQUIRE( 0 == bx::findFirstSet(0) ); + STATIC_REQUIRE(32 == bx::findFirstSet(1u<<31) ); + STATIC_REQUIRE( 1 == bx::findFirstSet(1) ); + STATIC_REQUIRE( 0 == bx::findFirstSet(0) ); + STATIC_REQUIRE(64 == bx::findFirstSet(0x8000000000000000ull) ); + STATIC_REQUIRE( 1 == bx::findFirstSet(0x8000000000000001ull) ); + + testFindFirstSet(); + testFindFirstSet(); + testFindFirstSet(); + testFindFirstSet(); +} + +template +void testFindLastSet() +{ + for (uint8_t ii = 0, num = sizeof(Ty)*8; ii < num; ++ii) + { + { + const Ty val = Ty(1) << ii; + const uint8_t result = bx::findLastSet(val); + REQUIRE(result == ii + 1); + } + + { + const Ty val = (Ty(1) << ii) - 1; + const uint8_t result = bx::findLastSet(val); + REQUIRE(result == ii); + } + } +} + +TEST_CASE("findLastSet", "[math]") +{ + STATIC_REQUIRE( 1 == bx::findLastSet(1) ); + STATIC_REQUIRE( 8 == bx::findLastSet(1<<7) ); + STATIC_REQUIRE( 0 == bx::findLastSet(0) ); + STATIC_REQUIRE( 6 == bx::findLastSet(0x3e) ); + STATIC_REQUIRE( 1 == bx::findLastSet(1) ); + STATIC_REQUIRE(16 == bx::findLastSet(1<<15) ); + STATIC_REQUIRE( 0 == bx::findLastSet(0) ); + STATIC_REQUIRE( 1 == bx::findLastSet(1) ); + STATIC_REQUIRE( 0 == bx::findLastSet(0) ); + STATIC_REQUIRE(32 == bx::findLastSet(1u<<31) ); + STATIC_REQUIRE( 1 == bx::findLastSet(1) ); + STATIC_REQUIRE( 0 == bx::findLastSet(0) ); + STATIC_REQUIRE( 1 == bx::findLastSet(1ull) ); + STATIC_REQUIRE(64 == bx::findLastSet(0x8000000000000000ull) ); + STATIC_REQUIRE(64 == bx::findLastSet(0x8000000000000001ull) ); + + testFindLastSet(); + testFindLastSet(); + testFindLastSet(); + testFindLastSet(); } BX_PRAGMA_DIAGNOSTIC_PUSH(); @@ -236,16 +304,16 @@ 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) ); + STATIC_REQUIRE(1.0f == bx::rcp(1.0f) ); + STATIC_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) ); + STATIC_REQUIRE(1.0f == bx::rcpSafe(1.0f) ); + STATIC_REQUIRE(2.0f == bx::rcpSafe(0.5f) ); REQUIRE(!bx::isInfinite(bx::rcpSafe( 0.0f) ) ); REQUIRE(!bx::isInfinite(bx::rcpSafe(-0.0f) ) ); } @@ -348,43 +416,43 @@ BX_PRAGMA_DIAGNOSTIC_POP(); TEST_CASE("abs", "[math][libm]") { - REQUIRE(1389.0f == bx::abs(-1389.0f) ); - REQUIRE(1389.0f == bx::abs( 1389.0f) ); - REQUIRE( 0.0f == bx::abs(-0.0f) ); - REQUIRE( 0.0f == bx::abs( 0.0f) ); + STATIC_REQUIRE(1389.0f == bx::abs(-1389.0f) ); + STATIC_REQUIRE(1389.0f == bx::abs( 1389.0f) ); + STATIC_REQUIRE( 0.0f == bx::abs(-0.0f) ); + STATIC_REQUIRE( 0.0f == bx::abs( 0.0f) ); } 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) ); + STATIC_REQUIRE(389.0f == bx::mod(1389.0f, 1000.0f) ); + STATIC_REQUIRE( 89.0f == bx::mod(1389.0f, 100.0f) ); + STATIC_REQUIRE( 9.0f == bx::mod(1389.0f, 10.0f) ); + STATIC_REQUIRE( 4.0f == bx::mod(1389.0f, 5.0f) ); + STATIC_REQUIRE( 1.0f == bx::mod(1389.0f, 2.0f) ); } TEST_CASE("floor", "[math][libm]") { - REQUIRE( 13.0f == bx::floor( 13.89f) ); - REQUIRE(-14.0f == bx::floor(-13.89f) ); + STATIC_REQUIRE( 13.0f == bx::floor( 13.89f) ); + STATIC_REQUIRE(-14.0f == bx::floor(-13.89f) ); } TEST_CASE("ceil", "[math][libm]") { - REQUIRE( 14.0f == bx::ceil( 13.89f) ); - REQUIRE(-13.0f == bx::ceil( -13.89f) ); + STATIC_REQUIRE( 14.0f == bx::ceil( 13.89f) ); + STATIC_REQUIRE(-13.0f == bx::ceil( -13.89f) ); } TEST_CASE("trunc", "[math][libm]") { - REQUIRE( 13.0f == bx::trunc( 13.89f) ); - REQUIRE(-13.0f == bx::trunc(-13.89f) ); + STATIC_REQUIRE( 13.0f == bx::trunc( 13.89f) ); + STATIC_REQUIRE(-13.0f == bx::trunc(-13.89f) ); } TEST_CASE("fract", "[math][libm]") { - REQUIRE(bx::isEqual( 0.89f, bx::fract( 13.89f), 0.000001f) ); - REQUIRE(bx::isEqual(-0.89f, bx::fract(-13.89f), 0.000001f) ); + STATIC_REQUIRE(bx::isEqual( 0.89f, bx::fract( 13.89f), 0.000001f) ); + STATIC_REQUIRE(bx::isEqual(-0.89f, bx::fract(-13.89f), 0.000001f) ); } TEST_CASE("ldexp", "[math][libm]") @@ -633,10 +701,10 @@ TEST_CASE("bitsToFloat, floatToBits, bitsToDouble, doubleToBits", "[math]") TEST_CASE("lerp", "[math]") { - REQUIRE(1389.0f == bx::lerp(1389.0f, 1453.0f, 0.0f) ); - REQUIRE(1453.0f == bx::lerp(1389.0f, 1453.0f, 1.0f) ); - REQUIRE( 0.5f == bx::lerp( 0.0f, 1.0f, 0.5f) ); - REQUIRE( 0.0f == bx::lerp( 0.0f, 0.0f, 0.5f) ); + STATIC_REQUIRE(1389.0f == bx::lerp(1389.0f, 1453.0f, 0.0f) ); + STATIC_REQUIRE(1453.0f == bx::lerp(1389.0f, 1453.0f, 1.0f) ); + STATIC_REQUIRE( 0.5f == bx::lerp( 0.0f, 1.0f, 0.5f) ); + STATIC_REQUIRE( 0.0f == bx::lerp( 0.0f, 0.0f, 0.5f) ); } void mtxCheck(const float* _a, const float* _b)