Browse Source

Added saturateCast. (#354)

Branimir Karadžić 1 day ago
parent
commit
4ac903bdcf
3 changed files with 164 additions and 3 deletions
  1. 10 0
      include/bx/bx.h
  2. 56 3
      include/bx/inline/bx.inl
  3. 98 0
      tests/cast_test.cpp

+ 10 - 0
include/bx/bx.h

@@ -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>

+ 56 - 3
include/bx/inline/bx.inl

@@ -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;

+ 98 - 0
tests/cast_test.cpp

@@ -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) );
+}