diff --git a/include/bx/bx.h b/include/bx/bx.h index 83b721f..9706862 100644 --- a/include/bx/bx.h +++ b/include/bx/bx.h @@ -224,6 +224,16 @@ namespace bx template constexpr Ty bitCast(const FromT& _from); + /// Performs `static_cast` of value `_from`, and if value doesn't fit result type `Ty` it clamps + /// the value to `Ty` min/max. + template + constexpr Ty saturateCast(const FromT& _from); + + /// Performs `static_cast` of value `_from`, and returns true if the value `_from` is + /// representable as `Ty`. + template + constexpr bool narrowCastTest(Ty* _out, const FromT& _from); + /// Performs `static_cast` of value `_from`, and in debug build runtime verifies/asserts /// that the value didn't change. template diff --git a/include/bx/inline/bx.inl b/include/bx/inline/bx.inl index e5f1214..c49e486 100644 --- a/include/bx/inline/bx.inl +++ b/include/bx/inline/bx.inl @@ -171,18 +171,71 @@ namespace bx return __builtin_bit_cast(Ty, _from); } + template + inline constexpr Ty saturateCast(const FromT& _from) + { + if constexpr (isSame() ) + { + return _from; + } + + constexpr Ty mx = max(); + + if constexpr (isSigned() && isSigned() ) + { + if constexpr (sizeof(Ty) < sizeof(FromT) ) + { + constexpr FromT mn = min(); + + if (_from < mn) + { + return mn; + } + else if (_from > mx) + { + return mx; + } + } + } + else if constexpr (isSigned() ) + { + if (_from < FromT(0) ) + { + return Ty(0); + } + else if (asUnsigned(_from) > mx) + { + return mx; + } + } + else if (_from > asUnsigned(max() ) ) + { + return mx; + } + + return static_cast(_from); + } + template inline constexpr bool narrowCastTest(Ty* _out, const FromT& _from) { - *_out = static_cast(_from); + if constexpr (isSame() ) + { + *_out = _from; + return true; + } + + *_out = saturateCast(_from); return static_cast(*_out) == _from; } template inline Ty narrowCast(const FromT& _from, Location _location) { - Ty to = static_cast(_from); - BX_ASSERT_LOC(_location, static_cast(to) == _from + Ty to; + const bool result = narrowCastTest(&to, _from); + + BX_ASSERT_LOC(_location, result , "bx::narrowCast failed! Value is truncated!" ); return to; diff --git a/tests/cast_test.cpp b/tests/cast_test.cpp index a48c9f9..ad2564e 100644 --- a/tests/cast_test.cpp +++ b/tests/cast_test.cpp @@ -21,3 +21,101 @@ TEST_CASE("Narrow cast", "[cast]") REQUIRE_ASSERTS(bx::narrowCast(uint32_t(128) ) ); REQUIRE(128 == bx::narrowCast(int32_t(128) ) ); } + +template +inline constexpr bool saturateCastTest(Ty _expectedMin, Ty _expectedMax) +{ + return true + && _expectedMin == bx::saturateCast(static_cast(_expectedMin) ) + && _expectedMax == bx::saturateCast(static_cast(_expectedMax) ) + ; +} + +TEST_CASE("Saturate cast", "[cast]") +{ + STATIC_REQUIRE(-128 == bx::saturateCast( int32_t( -255) ) ); + STATIC_REQUIRE( 127 == bx::saturateCast( int32_t( 255) ) ); + STATIC_REQUIRE( 127 == bx::saturateCast( int32_t( 127) ) ); + STATIC_REQUIRE( 127 == bx::saturateCast( int32_t( 128) ) ); + STATIC_REQUIRE( 127 == bx::saturateCast(uint32_t( 128) ) ); + STATIC_REQUIRE(-128 == bx::saturateCast( float(-1389.9831f) ) ); + STATIC_REQUIRE( 127 == bx::saturateCast( float( 1389.9831f) ) ); + STATIC_REQUIRE( 0 == bx::saturateCast( int32_t( -128) ) ); + STATIC_REQUIRE( 128 == bx::saturateCast( int32_t( 128) ) ); + STATIC_REQUIRE( -13 == bx::saturateCast( float( -13.89f) ) ); + STATIC_REQUIRE( 13 == bx::saturateCast( float( 13.89f) ) ); + STATIC_REQUIRE( 0 == bx::saturateCast( float( -13.89f) ) ); + STATIC_REQUIRE( 13 == bx::saturateCast( float( 13.89f) ) ); + + STATIC_REQUIRE(saturateCastTest(-128, 127) ); + STATIC_REQUIRE(saturateCastTest( 0, 127) ); + STATIC_REQUIRE(saturateCastTest(-128, 127) ); + STATIC_REQUIRE(saturateCastTest( 0, 127) ); + STATIC_REQUIRE(saturateCastTest(-128, 127) ); + STATIC_REQUIRE(saturateCastTest( 0, 127) ); + STATIC_REQUIRE(saturateCastTest(-128, 127) ); + STATIC_REQUIRE(saturateCastTest( 0, 127) ); + + STATIC_REQUIRE(saturateCastTest( 0, 127) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + + STATIC_REQUIRE(saturateCastTest( -128, 127) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + STATIC_REQUIRE(saturateCastTest( -32768, 32767) ); + STATIC_REQUIRE(saturateCastTest( 0, 32767) ); + STATIC_REQUIRE(saturateCastTest( -32768, 32767) ); + STATIC_REQUIRE(saturateCastTest( 0, 32767) ); + STATIC_REQUIRE(saturateCastTest( -32768, 32767) ); + STATIC_REQUIRE(saturateCastTest( 0, 32767) ); + + STATIC_REQUIRE(saturateCastTest( 0, 127) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + STATIC_REQUIRE(saturateCastTest( 0, 32767) ); + STATIC_REQUIRE(saturateCastTest( 0, 65535) ); + STATIC_REQUIRE(saturateCastTest( 0, 65535) ); + STATIC_REQUIRE(saturateCastTest( 0, 65535) ); + STATIC_REQUIRE(saturateCastTest( 0, 65535) ); + STATIC_REQUIRE(saturateCastTest( 0, 65535) ); + + STATIC_REQUIRE(saturateCastTest( -128, 127) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + STATIC_REQUIRE(saturateCastTest( -32768, 32767) ); + STATIC_REQUIRE(saturateCastTest( 0, 65535) ); + STATIC_REQUIRE(saturateCastTest( -2147483648, 2147483647) ); + STATIC_REQUIRE(saturateCastTest( 0, 2147483647) ); + STATIC_REQUIRE(saturateCastTest( -2147483648, 2147483647) ); + STATIC_REQUIRE(saturateCastTest( 0, 2147483647) ); + + STATIC_REQUIRE(saturateCastTest( 0, 127) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + STATIC_REQUIRE(saturateCastTest( 0, 32767) ); + STATIC_REQUIRE(saturateCastTest( 0, 65535) ); + STATIC_REQUIRE(saturateCastTest( 0, 2147483647) ); + STATIC_REQUIRE(saturateCastTest( 0, 4294967295) ); + STATIC_REQUIRE(saturateCastTest( 0, 4294967295) ); + STATIC_REQUIRE(saturateCastTest( 0, 4294967295) ); + + STATIC_REQUIRE(saturateCastTest( -128, 127) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + STATIC_REQUIRE(saturateCastTest( -32768, 32767) ); + STATIC_REQUIRE(saturateCastTest( 0, 65535) ); + STATIC_REQUIRE(saturateCastTest( -2147483648, 2147483647) ); + STATIC_REQUIRE(saturateCastTest( 0, 4294967295) ); + STATIC_REQUIRE(saturateCastTest( -9223372036854775808ll, 9223372036854775807ll) ); + STATIC_REQUIRE(saturateCastTest( 0, 9223372036854775807ll) ); + + STATIC_REQUIRE(saturateCastTest( 0, 127) ); + STATIC_REQUIRE(saturateCastTest( 0, 255) ); + STATIC_REQUIRE(saturateCastTest( 0, 32767) ); + STATIC_REQUIRE(saturateCastTest( 0, 65535) ); + STATIC_REQUIRE(saturateCastTest( 0, 2147483647) ); + STATIC_REQUIRE(saturateCastTest( 0, 4294967295) ); + STATIC_REQUIRE(saturateCastTest( 0, 9223372036854775807ull) ); + STATIC_REQUIRE(saturateCastTest( 0, 18446744073709551615ull) ); +}