/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_ENDIAN_FLOATINGPOINT_HPP #define MPT_ENDIAN_FLOATINGPOINT_HPP #include "mpt/base/bit.hpp" #include "mpt/base/floatingpoint.hpp" #include "mpt/base/macros.hpp" #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include #include #include #include #include namespace mpt { inline namespace MPT_INLINE_NS { // 1.0f --> 0x3f800000u MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f) { if constexpr (mpt::float_traits::is_ieee754_binary32ne) { return mpt::bit_cast(f); } else { int e = 0; float m = std::frexp(f, &e); if (e == 0 && std::fabs(m) == 0.0f) { uint32 expo = 0u; uint32 sign = std::signbit(m) ? 0x01u : 0x00u; uint32 mant = 0u; uint32 i = 0u; i |= (mant << 0) & 0x007fffffu; i |= (expo << 23) & 0x7f800000u; i |= (sign << 31) & 0x80000000u; return i; } else { uint32 expo = e + 127 - 1; uint32 sign = std::signbit(m) ? 0x01u : 0x00u; uint32 mant = static_cast(std::fabs(std::ldexp(m, 24))); uint32 i = 0u; i |= (mant << 0) & 0x007fffffu; i |= (expo << 23) & 0x7f800000u; i |= (sign << 31) & 0x80000000u; return i; } } } MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f) { if constexpr (mpt::float_traits::is_ieee754_binary64ne) { return mpt::bit_cast(f); } else { int e = 0; double m = std::frexp(f, &e); if (e == 0 && std::fabs(m) == 0.0) { uint64 expo = 0u; uint64 sign = std::signbit(m) ? 0x01u : 0x00u; uint64 mant = 0u; uint64 i = 0u; i |= (mant << 0) & 0x000fffffffffffffull; i |= (expo << 52) & 0x7ff0000000000000ull; i |= (sign << 63) & 0x8000000000000000ull; return i; } else { uint64 expo = static_cast(e) + 1023 - 1; uint64 sign = std::signbit(m) ? 0x01u : 0x00u; uint64 mant = static_cast(std::fabs(std::ldexp(m, 53))); uint64 i = 0u; i |= (mant << 0) & 0x000fffffffffffffull; i |= (expo << 52) & 0x7ff0000000000000ull; i |= (sign << 63) & 0x8000000000000000ull; return i; } } } // 0x3f800000u --> 1.0f MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i) { if constexpr (mpt::float_traits::is_ieee754_binary32ne) { return mpt::bit_cast(i); } else { uint32 mant = (i & 0x007fffffu) >> 0; uint32 expo = (i & 0x7f800000u) >> 23; uint32 sign = (i & 0x80000000u) >> 31; if (expo == 0) { float m = sign ? -static_cast(mant) : static_cast(mant); int e = static_cast(expo) - 127 + 1 - 24; float f = std::ldexp(m, e); return static_cast(f); } else { mant |= 0x00800000u; float m = sign ? -static_cast(mant) : static_cast(mant); int e = static_cast(expo) - 127 + 1 - 24; float f = std::ldexp(m, e); return static_cast(f); } } } MPT_FORCEINLINE float64 DecodeIEEE754binary64(uint64 i) { if constexpr (mpt::float_traits::is_ieee754_binary64ne) { return mpt::bit_cast(i); } else { uint64 mant = (i & 0x000fffffffffffffull) >> 0; uint64 expo = (i & 0x7ff0000000000000ull) >> 52; uint64 sign = (i & 0x8000000000000000ull) >> 63; if (expo == 0) { double m = sign ? -static_cast(mant) : static_cast(mant); int e = static_cast(expo) - 1023 + 1 - 53; double f = std::ldexp(m, e); return static_cast(f); } else { mant |= 0x0010000000000000ull; double m = sign ? -static_cast(mant) : static_cast(mant); int e = static_cast(expo) - 1023 + 1 - 53; double f = std::ldexp(m, e); return static_cast(f); } } } // template parameters are byte indices corresponding to the individual bytes of iee754 in memory template struct IEEE754binary32Emulated { public: using self_t = IEEE754binary32Emulated; std::byte bytes[4]; public: MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { return bytes[i]; } IEEE754binary32Emulated() = default; MPT_FORCEINLINE explicit IEEE754binary32Emulated(float32 f) { SetInt32(EncodeIEEE754binary32(f)); } // b0...b3 are in memory order, i.e. depend on the endianness of this type // little endian: (0x00,0x00,0x80,0x3f) // big endian: (0x3f,0x80,0x00,0x00) MPT_FORCEINLINE explicit IEEE754binary32Emulated(std::byte b0, std::byte b1, std::byte b2, std::byte b3) { bytes[0] = b0; bytes[1] = b1; bytes[2] = b2; bytes[3] = b3; } MPT_FORCEINLINE operator float32() const { return DecodeIEEE754binary32(GetInt32()); } MPT_FORCEINLINE self_t & SetInt32(uint32 i) { bytes[hihi] = static_cast(i >> 24); bytes[hilo] = static_cast(i >> 16); bytes[lohi] = static_cast(i >> 8); bytes[lolo] = static_cast(i >> 0); return *this; } MPT_FORCEINLINE uint32 GetInt32() const { return 0u | (static_cast(bytes[hihi]) << 24) | (static_cast(bytes[hilo]) << 16) | (static_cast(bytes[lohi]) << 8) | (static_cast(bytes[lolo]) << 0); } MPT_FORCEINLINE bool operator==(const self_t & cmp) const { return true && bytes[0] == cmp.bytes[0] && bytes[1] == cmp.bytes[1] && bytes[2] == cmp.bytes[2] && bytes[3] == cmp.bytes[3]; } MPT_FORCEINLINE bool operator!=(const self_t & cmp) const { return !(*this == cmp); } }; template struct IEEE754binary64Emulated { public: using self_t = IEEE754binary64Emulated; std::byte bytes[8]; public: MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { return bytes[i]; } IEEE754binary64Emulated() = default; MPT_FORCEINLINE explicit IEEE754binary64Emulated(float64 f) { SetInt64(EncodeIEEE754binary64(f)); } MPT_FORCEINLINE explicit IEEE754binary64Emulated(std::byte b0, std::byte b1, std::byte b2, std::byte b3, std::byte b4, std::byte b5, std::byte b6, std::byte b7) { bytes[0] = b0; bytes[1] = b1; bytes[2] = b2; bytes[3] = b3; bytes[4] = b4; bytes[5] = b5; bytes[6] = b6; bytes[7] = b7; } MPT_FORCEINLINE operator float64() const { return DecodeIEEE754binary64(GetInt64()); } MPT_FORCEINLINE self_t & SetInt64(uint64 i) { bytes[hihihi] = static_cast(i >> 56); bytes[hihilo] = static_cast(i >> 48); bytes[hilohi] = static_cast(i >> 40); bytes[hilolo] = static_cast(i >> 32); bytes[lohihi] = static_cast(i >> 24); bytes[lohilo] = static_cast(i >> 16); bytes[lolohi] = static_cast(i >> 8); bytes[lololo] = static_cast(i >> 0); return *this; } MPT_FORCEINLINE uint64 GetInt64() const { return 0u | (static_cast(bytes[hihihi]) << 56) | (static_cast(bytes[hihilo]) << 48) | (static_cast(bytes[hilohi]) << 40) | (static_cast(bytes[hilolo]) << 32) | (static_cast(bytes[lohihi]) << 24) | (static_cast(bytes[lohilo]) << 16) | (static_cast(bytes[lolohi]) << 8) | (static_cast(bytes[lololo]) << 0); } MPT_FORCEINLINE bool operator==(const self_t & cmp) const { return true && bytes[0] == cmp.bytes[0] && bytes[1] == cmp.bytes[1] && bytes[2] == cmp.bytes[2] && bytes[3] == cmp.bytes[3] && bytes[4] == cmp.bytes[4] && bytes[5] == cmp.bytes[5] && bytes[6] == cmp.bytes[6] && bytes[7] == cmp.bytes[7]; } MPT_FORCEINLINE bool operator!=(const self_t & cmp) const { return !(*this == cmp); } }; using IEEE754binary32EmulatedBE = IEEE754binary32Emulated<0, 1, 2, 3>; using IEEE754binary32EmulatedLE = IEEE754binary32Emulated<3, 2, 1, 0>; using IEEE754binary64EmulatedBE = IEEE754binary64Emulated<0, 1, 2, 3, 4, 5, 6, 7>; using IEEE754binary64EmulatedLE = IEEE754binary64Emulated<7, 6, 5, 4, 3, 2, 1, 0>; constexpr bool declare_binary_safe(const IEEE754binary32EmulatedBE &) { return true; } constexpr bool declare_binary_safe(const IEEE754binary32EmulatedLE &) { return true; } constexpr bool declare_binary_safe(const IEEE754binary64EmulatedBE &) { return true; } constexpr bool declare_binary_safe(const IEEE754binary64EmulatedLE &) { return true; } static_assert(mpt::check_binary_size(4)); static_assert(mpt::check_binary_size(4)); static_assert(mpt::check_binary_size(8)); static_assert(mpt::check_binary_size(8)); template struct IEEE754binary32Native { public: float32 value; public: MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { static_assert(endian == mpt::endian::little || endian == mpt::endian::big); if constexpr (endian == mpt::endian::little) { return static_cast(EncodeIEEE754binary32(value) >> (i * 8)); } if constexpr (endian == mpt::endian::big) { return static_cast(EncodeIEEE754binary32(value) >> ((4 - 1 - i) * 8)); } } IEEE754binary32Native() = default; MPT_FORCEINLINE explicit IEEE754binary32Native(float32 f) { value = f; } // b0...b3 are in memory order, i.e. depend on the endianness of this type // little endian: (0x00,0x00,0x80,0x3f) // big endian: (0x3f,0x80,0x00,0x00) MPT_FORCEINLINE explicit IEEE754binary32Native(std::byte b0, std::byte b1, std::byte b2, std::byte b3) { static_assert(endian == mpt::endian::little || endian == mpt::endian::big); if constexpr (endian == mpt::endian::little) { value = DecodeIEEE754binary32(0u | (static_cast(b0) << 0) | (static_cast(b1) << 8) | (static_cast(b2) << 16) | (static_cast(b3) << 24)); } if constexpr (endian == mpt::endian::big) { value = DecodeIEEE754binary32(0u | (static_cast(b0) << 24) | (static_cast(b1) << 16) | (static_cast(b2) << 8) | (static_cast(b3) << 0)); } } MPT_FORCEINLINE operator float32() const { return value; } MPT_FORCEINLINE IEEE754binary32Native & SetInt32(uint32 i) { value = DecodeIEEE754binary32(i); return *this; } MPT_FORCEINLINE uint32 GetInt32() const { return EncodeIEEE754binary32(value); } MPT_FORCEINLINE bool operator==(const IEEE754binary32Native & cmp) const { return value == cmp.value; } MPT_FORCEINLINE bool operator!=(const IEEE754binary32Native & cmp) const { return value != cmp.value; } }; template struct IEEE754binary64Native { public: float64 value; public: MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { static_assert(endian == mpt::endian::little || endian == mpt::endian::big); if constexpr (endian == mpt::endian::little) { return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> (i * 8))); } if constexpr (endian == mpt::endian::big) { return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> ((8 - 1 - i) * 8))); } } IEEE754binary64Native() = default; MPT_FORCEINLINE explicit IEEE754binary64Native(float64 f) { value = f; } MPT_FORCEINLINE explicit IEEE754binary64Native(std::byte b0, std::byte b1, std::byte b2, std::byte b3, std::byte b4, std::byte b5, std::byte b6, std::byte b7) { static_assert(endian == mpt::endian::little || endian == mpt::endian::big); if constexpr (endian == mpt::endian::little) { value = DecodeIEEE754binary64(0ull | (static_cast(b0) << 0) | (static_cast(b1) << 8) | (static_cast(b2) << 16) | (static_cast(b3) << 24) | (static_cast(b4) << 32) | (static_cast(b5) << 40) | (static_cast(b6) << 48) | (static_cast(b7) << 56)); } if constexpr (endian == mpt::endian::big) { value = DecodeIEEE754binary64(0ull | (static_cast(b0) << 56) | (static_cast(b1) << 48) | (static_cast(b2) << 40) | (static_cast(b3) << 32) | (static_cast(b4) << 24) | (static_cast(b5) << 16) | (static_cast(b6) << 8) | (static_cast(b7) << 0)); } } MPT_FORCEINLINE operator float64() const { return value; } MPT_FORCEINLINE IEEE754binary64Native & SetInt64(uint64 i) { value = DecodeIEEE754binary64(i); return *this; } MPT_FORCEINLINE uint64 GetInt64() const { return EncodeIEEE754binary64(value); } MPT_FORCEINLINE bool operator==(const IEEE754binary64Native & cmp) const { return value == cmp.value; } MPT_FORCEINLINE bool operator!=(const IEEE754binary64Native & cmp) const { return value != cmp.value; } }; static_assert((sizeof(IEEE754binary32Native<>) == 4)); static_assert((sizeof(IEEE754binary64Native<>) == 8)); constexpr bool declare_binary_safe(const IEEE754binary32Native<> &) noexcept { return true; } constexpr bool declare_binary_safe(const IEEE754binary64Native<> &) noexcept { return true; } template struct IEEE754binary_types { using IEEE754binary32LE = IEEE754binary32EmulatedLE; using IEEE754binary32BE = IEEE754binary32EmulatedBE; using IEEE754binary64LE = IEEE754binary64EmulatedLE; using IEEE754binary64BE = IEEE754binary64EmulatedBE; }; template <> struct IEEE754binary_types { using IEEE754binary32LE = IEEE754binary32Native<>; using IEEE754binary32BE = IEEE754binary32EmulatedBE; using IEEE754binary64LE = IEEE754binary64Native<>; using IEEE754binary64BE = IEEE754binary64EmulatedBE; }; template <> struct IEEE754binary_types { using IEEE754binary32LE = IEEE754binary32EmulatedLE; using IEEE754binary32BE = IEEE754binary32Native<>; using IEEE754binary64LE = IEEE754binary64EmulatedLE; using IEEE754binary64BE = IEEE754binary64Native<>; }; using IEEE754binary32LE = IEEE754binary_types::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32LE; using IEEE754binary32BE = IEEE754binary_types::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32BE; using IEEE754binary64LE = IEEE754binary_types::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64LE; using IEEE754binary64BE = IEEE754binary_types::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64BE; static_assert(sizeof(IEEE754binary32LE) == 4); static_assert(sizeof(IEEE754binary32BE) == 4); static_assert(sizeof(IEEE754binary64LE) == 8); static_assert(sizeof(IEEE754binary64BE) == 8); // unaligned using float32le = IEEE754binary32EmulatedLE; using float32be = IEEE754binary32EmulatedBE; using float64le = IEEE754binary64EmulatedLE; using float64be = IEEE754binary64EmulatedBE; static_assert(sizeof(float32le) == 4); static_assert(sizeof(float32be) == 4); static_assert(sizeof(float64le) == 8); static_assert(sizeof(float64be) == 8); // potentially aligned using float32le_fast = IEEE754binary32LE; using float32be_fast = IEEE754binary32BE; using float64le_fast = IEEE754binary64LE; using float64be_fast = IEEE754binary64BE; static_assert(sizeof(float32le_fast) == 4); static_assert(sizeof(float32be_fast) == 4); static_assert(sizeof(float64le_fast) == 8); static_assert(sizeof(float64be_fast) == 8); template struct make_float_be { }; template <> struct make_float_be { using type = IEEE754binary64BE; }; template <> struct make_float_be { using type = IEEE754binary32BE; }; template struct make_float_le { }; template <> struct make_float_le { using type = IEEE754binary64LE; }; template <> struct make_float_le { using type = IEEE754binary32LE; }; template struct make_float_endian { }; template struct make_float_endian { using type = typename make_float_le::type>::type; }; template struct make_float_endian { using type = typename make_float_be::type>::type; }; } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_ENDIAN_FLOATINGPOINT_HPP