Added saturateCast. (#354)

This commit is contained in:
Branimir Karadžić
2025-12-13 11:05:24 -08:00
committed by GitHub
parent 5dc415ee2e
commit 4ac903bdcf
3 changed files with 164 additions and 3 deletions

View File

@@ -224,6 +224,16 @@ namespace bx
template <typename Ty, typename FromT>
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<typename Ty, typename FromT>
constexpr Ty saturateCast(const FromT& _from);
/// Performs `static_cast` of value `_from`, and returns true if the value `_from` is
/// representable as `Ty`.
template<typename Ty, typename FromT>
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<typename Ty, typename FromT>

View File

@@ -171,18 +171,71 @@ namespace bx
return __builtin_bit_cast(Ty, _from);
}
template<typename Ty, typename FromT>
inline constexpr Ty saturateCast(const FromT& _from)
{
if constexpr (isSame<Ty, FromT>() )
{
return _from;
}
constexpr Ty mx = max<Ty>();
if constexpr (isSigned<Ty>() && isSigned<FromT>() )
{
if constexpr (sizeof(Ty) < sizeof(FromT) )
{
constexpr FromT mn = min<Ty>();
if (_from < mn)
{
return mn;
}
else if (_from > mx)
{
return mx;
}
}
}
else if constexpr (isSigned<FromT>() )
{
if (_from < FromT(0) )
{
return Ty(0);
}
else if (asUnsigned<FromT>(_from) > mx)
{
return mx;
}
}
else if (_from > asUnsigned<Ty>(max<Ty>() ) )
{
return mx;
}
return static_cast<Ty>(_from);
}
template<typename Ty, typename FromT>
inline constexpr bool narrowCastTest(Ty* _out, const FromT& _from)
{
*_out = static_cast<Ty>(_from);
if constexpr (isSame<Ty, FromT>() )
{
*_out = _from;
return true;
}
*_out = saturateCast<Ty>(_from);
return static_cast<FromT>(*_out) == _from;
}
template<typename Ty, typename FromT>
inline Ty narrowCast(const FromT& _from, Location _location)
{
Ty to = static_cast<Ty>(_from);
BX_ASSERT_LOC(_location, static_cast<FromT>(to) == _from
Ty to;
const bool result = narrowCastTest(&to, _from);
BX_ASSERT_LOC(_location, result
, "bx::narrowCast failed! Value is truncated!"
);
return to;

View File

@@ -21,3 +21,101 @@ TEST_CASE("Narrow cast", "[cast]")
REQUIRE_ASSERTS(bx::narrowCast<int8_t>(uint32_t(128) ) );
REQUIRE(128 == bx::narrowCast<uint8_t>(int32_t(128) ) );
}
template<typename Ty, typename FromT>
inline constexpr bool saturateCastTest(Ty _expectedMin, Ty _expectedMax)
{
return true
&& _expectedMin == bx::saturateCast<Ty>(static_cast<FromT>(_expectedMin) )
&& _expectedMax == bx::saturateCast<Ty>(static_cast<FromT>(_expectedMax) )
;
}
TEST_CASE("Saturate cast", "[cast]")
{
STATIC_REQUIRE(-128 == bx::saturateCast<int8_t >( int32_t( -255) ) );
STATIC_REQUIRE( 127 == bx::saturateCast<int8_t >( int32_t( 255) ) );
STATIC_REQUIRE( 127 == bx::saturateCast<int8_t >( int32_t( 127) ) );
STATIC_REQUIRE( 127 == bx::saturateCast<int8_t >( int32_t( 128) ) );
STATIC_REQUIRE( 127 == bx::saturateCast<int8_t >(uint32_t( 128) ) );
STATIC_REQUIRE(-128 == bx::saturateCast<int8_t >( float(-1389.9831f) ) );
STATIC_REQUIRE( 127 == bx::saturateCast<int8_t >( float( 1389.9831f) ) );
STATIC_REQUIRE( 0 == bx::saturateCast<uint8_t>( int32_t( -128) ) );
STATIC_REQUIRE( 128 == bx::saturateCast<uint8_t>( int32_t( 128) ) );
STATIC_REQUIRE( -13 == bx::saturateCast<int8_t >( float( -13.89f) ) );
STATIC_REQUIRE( 13 == bx::saturateCast<int8_t >( float( 13.89f) ) );
STATIC_REQUIRE( 0 == bx::saturateCast<uint8_t>( float( -13.89f) ) );
STATIC_REQUIRE( 13 == bx::saturateCast<uint8_t>( float( 13.89f) ) );
STATIC_REQUIRE(saturateCastTest<int8_t, int8_t>(-128, 127) );
STATIC_REQUIRE(saturateCastTest<int8_t, uint8_t>( 0, 127) );
STATIC_REQUIRE(saturateCastTest<int8_t, int16_t>(-128, 127) );
STATIC_REQUIRE(saturateCastTest<int8_t, uint16_t>( 0, 127) );
STATIC_REQUIRE(saturateCastTest<int8_t, int32_t>(-128, 127) );
STATIC_REQUIRE(saturateCastTest<int8_t, uint32_t>( 0, 127) );
STATIC_REQUIRE(saturateCastTest<int8_t, int64_t>(-128, 127) );
STATIC_REQUIRE(saturateCastTest<int8_t, uint64_t>( 0, 127) );
STATIC_REQUIRE(saturateCastTest<uint8_t, int8_t>( 0, 127) );
STATIC_REQUIRE(saturateCastTest<uint8_t, uint8_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<uint8_t, int16_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<uint8_t, uint16_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<uint8_t, int32_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<uint8_t, uint32_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<uint8_t, int64_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<uint8_t, uint64_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<int16_t, int8_t>( -128, 127) );
STATIC_REQUIRE(saturateCastTest<int16_t, uint8_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<int16_t, int16_t>( -32768, 32767) );
STATIC_REQUIRE(saturateCastTest<int16_t, uint16_t>( 0, 32767) );
STATIC_REQUIRE(saturateCastTest<int16_t, int32_t>( -32768, 32767) );
STATIC_REQUIRE(saturateCastTest<int16_t, uint32_t>( 0, 32767) );
STATIC_REQUIRE(saturateCastTest<int16_t, int64_t>( -32768, 32767) );
STATIC_REQUIRE(saturateCastTest<int16_t, uint64_t>( 0, 32767) );
STATIC_REQUIRE(saturateCastTest<uint16_t, int8_t>( 0, 127) );
STATIC_REQUIRE(saturateCastTest<uint16_t, uint8_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<uint16_t, int16_t>( 0, 32767) );
STATIC_REQUIRE(saturateCastTest<uint16_t, uint16_t>( 0, 65535) );
STATIC_REQUIRE(saturateCastTest<uint16_t, int32_t>( 0, 65535) );
STATIC_REQUIRE(saturateCastTest<uint16_t, uint32_t>( 0, 65535) );
STATIC_REQUIRE(saturateCastTest<uint16_t, int64_t>( 0, 65535) );
STATIC_REQUIRE(saturateCastTest<uint16_t, uint64_t>( 0, 65535) );
STATIC_REQUIRE(saturateCastTest<int32_t, int8_t>( -128, 127) );
STATIC_REQUIRE(saturateCastTest<int32_t, uint8_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<int32_t, int16_t>( -32768, 32767) );
STATIC_REQUIRE(saturateCastTest<int32_t, uint16_t>( 0, 65535) );
STATIC_REQUIRE(saturateCastTest<int32_t, int32_t>( -2147483648, 2147483647) );
STATIC_REQUIRE(saturateCastTest<int32_t, uint32_t>( 0, 2147483647) );
STATIC_REQUIRE(saturateCastTest<int32_t, int64_t>( -2147483648, 2147483647) );
STATIC_REQUIRE(saturateCastTest<int32_t, uint64_t>( 0, 2147483647) );
STATIC_REQUIRE(saturateCastTest<uint32_t, int8_t>( 0, 127) );
STATIC_REQUIRE(saturateCastTest<uint32_t, uint8_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<uint32_t, int16_t>( 0, 32767) );
STATIC_REQUIRE(saturateCastTest<uint32_t, uint16_t>( 0, 65535) );
STATIC_REQUIRE(saturateCastTest<uint32_t, int32_t>( 0, 2147483647) );
STATIC_REQUIRE(saturateCastTest<uint32_t, uint32_t>( 0, 4294967295) );
STATIC_REQUIRE(saturateCastTest<uint32_t, int64_t>( 0, 4294967295) );
STATIC_REQUIRE(saturateCastTest<uint32_t, uint64_t>( 0, 4294967295) );
STATIC_REQUIRE(saturateCastTest<int64_t, int8_t>( -128, 127) );
STATIC_REQUIRE(saturateCastTest<int64_t, uint8_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<int64_t, int16_t>( -32768, 32767) );
STATIC_REQUIRE(saturateCastTest<int64_t, uint16_t>( 0, 65535) );
STATIC_REQUIRE(saturateCastTest<int64_t, int32_t>( -2147483648, 2147483647) );
STATIC_REQUIRE(saturateCastTest<int64_t, uint32_t>( 0, 4294967295) );
STATIC_REQUIRE(saturateCastTest<int64_t, int64_t>( -9223372036854775808ll, 9223372036854775807ll) );
STATIC_REQUIRE(saturateCastTest<int64_t, uint64_t>( 0, 9223372036854775807ll) );
STATIC_REQUIRE(saturateCastTest<uint64_t, int8_t>( 0, 127) );
STATIC_REQUIRE(saturateCastTest<uint64_t, uint8_t>( 0, 255) );
STATIC_REQUIRE(saturateCastTest<uint64_t, int16_t>( 0, 32767) );
STATIC_REQUIRE(saturateCastTest<uint64_t, uint16_t>( 0, 65535) );
STATIC_REQUIRE(saturateCastTest<uint64_t, int32_t>( 0, 2147483647) );
STATIC_REQUIRE(saturateCastTest<uint64_t, uint32_t>( 0, 4294967295) );
STATIC_REQUIRE(saturateCastTest<uint64_t, int64_t>( 0, 9223372036854775807ull) );
STATIC_REQUIRE(saturateCastTest<uint64_t, uint64_t>( 0, 18446744073709551615ull) );
}