diff --git a/Source/Core/Common/BitUtils.h b/Source/Core/Common/BitUtils.h index 2d45902e41..c975b5c475 100644 --- a/Source/Core/Common/BitUtils.h +++ b/Source/Core/Common/BitUtils.h @@ -6,6 +6,7 @@ #include #include +#include #include namespace Common @@ -165,4 +166,37 @@ constexpr bool IsValidLowMask(const T mask) noexcept // and doesn't require special casing either edge case. return (mask & (mask + 1)) == 0; } + +/// +/// Reinterpret objects of one type as another by bit-casting between object representations. +/// +/// @remark This is the example implementation of std::bit_cast which is to be included +/// in C++2a. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0476r2.html +/// for more details. The only difference is this variant is not constexpr, +/// as the mechanism for bit_cast requires a compiler built-in to have that quality. +/// +/// @param source The source object to convert to another representation. +/// +/// @tparam To The type to reinterpret source as. +/// @tparam From The initial type representation of source. +/// +/// @return The representation of type From as type To. +/// +/// @pre Both To and From types must be the same size +/// @pre Both To and From types must satisfy the TriviallyCopyable concept. +/// +template +inline To BitCast(const From& source) noexcept +{ + static_assert(sizeof(From) == sizeof(To), + "BitCast source and destination types must be equal in size."); + static_assert(std::is_trivially_copyable(), + "BitCast source type must be trivially copyable."); + static_assert(std::is_trivially_copyable(), + "BitCast destination type must be trivially copyable."); + + std::aligned_storage_t storage; + std::memcpy(&storage, &source, sizeof(storage)); + return reinterpret_cast(storage); +} } // namespace Common diff --git a/Source/UnitTests/Common/BitUtilsTest.cpp b/Source/UnitTests/Common/BitUtilsTest.cpp index 4785f9dcef..e847e908bd 100644 --- a/Source/UnitTests/Common/BitUtilsTest.cpp +++ b/Source/UnitTests/Common/BitUtilsTest.cpp @@ -127,3 +127,16 @@ TEST(BitUtils, IsValidLowMask) EXPECT_FALSE(Common::IsValidLowMask((u64) ~(0b10000))); EXPECT_FALSE(Common::IsValidLowMask((u64)(~((u64)(~0b0) >> 1) | 0b1111))); } + +TEST(BitUtils, BitCast) +{ + EXPECT_EQ(0x00000000U, Common::BitCast(0.0f)); + EXPECT_EQ(0x80000000U, Common::BitCast(-0.0f)); + EXPECT_EQ(0x3F800000U, Common::BitCast(1.0f)); + EXPECT_EQ(0xBF800000U, Common::BitCast(-1.0f)); + + EXPECT_EQ(0x0000000000000000ULL, Common::BitCast(0.0)); + EXPECT_EQ(0x8000000000000000ULL, Common::BitCast(-0.0)); + EXPECT_EQ(0x3FF0000000000000ULL, Common::BitCast(1.0)); + EXPECT_EQ(0xBFF0000000000000ULL, Common::BitCast(-1.0)); +}