#pragma once #include "CoreTypes.h" #include "Numeric/Bit.h" #include "Numeric/Limits.h" #include "TypeTraits/TypeTraits.h" #include "Miscellaneous/AssertionMacros.h" NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) NAMESPACE_BEGIN(Math) NAMESPACE_PRIVATE_BEGIN template struct TFloatingTypeTraits { static_assert(sizeof(T) == -1, "Unsupported floating point type."); }; template <> struct TFloatingTypeTraits { // IEEE-754 single precision floating point format. // SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM using FIntegralT = uint32; static constexpr int SignBits = 1; static constexpr int ExponentBits = 8; static constexpr int MantissaBits = 23; static_assert(SignBits + ExponentBits + MantissaBits == sizeof(float) * 8); static constexpr int ExponentBias = 127; static constexpr int SignShift = 31; static constexpr int ExponentShift = 23; static constexpr int MantissaShift = 0; static constexpr FIntegralT SignMask = 0x80000000; static constexpr FIntegralT ExponentMask = 0x7F800000; static constexpr FIntegralT MantissaMask = 0x007FFFFF; }; template <> struct TFloatingTypeTraits { // IEEE-754 double precision floating point format. // SEEEEEEE EEEEMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM using FIntegralT = uint64; static constexpr int SignBits = 1; static constexpr int ExponentBits = 11; static constexpr int MantissaBits = 52; static_assert(SignBits + ExponentBits + MantissaBits == sizeof(double) * 8); static constexpr int ExponentBias = 1023; static constexpr int SignShift = 63; static constexpr int ExponentShift = 52; static constexpr int MantissaShift = 0; static constexpr FIntegralT SignMask = 0x8000000000000000; static constexpr FIntegralT ExponentMask = 0x7FF0000000000000; static constexpr FIntegralT MantissaMask = 0x000FFFFFFFFFFFFF; }; NAMESPACE_PRIVATE_END #define RESOLVE_ARITHMETIC_AMBIGUITY_2_ARGS(Concept, Func) \ template requires (CCommonType) \ FORCEINLINE constexpr auto Func(T A, U B) \ { \ return Math::Func>(A, B); \ } #define RESOLVE_ARITHMETIC_AMBIGUITY_3_ARGS(Concept, Func) \ template requires (CCommonType) \ FORCEINLINE constexpr auto Func(T A, U B, V C) \ { \ return Math::Func>(A, B, C); \ } template FORCEINLINE constexpr T Abs(T A) { return A < 0 ? -A : A; } template FORCEINLINE constexpr T Abs(T A) { return A; } template FORCEINLINE constexpr T Sign(T A) { if (A == static_cast(0)) return static_cast( 0); if (A < static_cast(0)) return static_cast(-1); return static_cast(1); } template requires (CCommonType) FORCEINLINE constexpr auto Min(T A, Ts... InOther) { if constexpr (sizeof...(Ts) == 0) return A; else { using FCommonT = TCommonType; FCommonT B = Math::Min(InOther...); return A < B ? A : B; } } template requires (CCommonType) FORCEINLINE constexpr auto Max(T A, Ts... InOther) { if constexpr (sizeof...(Ts) == 0) return A; else { using FCommonT = TCommonType; FCommonT B = Math::Max(InOther...); return A > B ? A : B; } } template FORCEINLINE constexpr auto Div(T LHS, T RHS) { checkf(RHS != 0, TEXT("Illegal divisor. It must not be zero.")); struct { T Quotient; T Remainder; } Result; Result.Quotient = LHS / RHS; Result.Remainder = LHS % RHS; return Result; } RESOLVE_ARITHMETIC_AMBIGUITY_2_ARGS(CIntegral, Div) template FORCEINLINE constexpr bool IsNearlyEqual(T LHS, T RHS, T Epsilon = TNumericLimits::Epsilon()) { return Math::Abs(LHS - RHS) <= Epsilon; } RESOLVE_ARITHMETIC_AMBIGUITY_2_ARGS(CArithmetic, IsNearlyEqual) RESOLVE_ARITHMETIC_AMBIGUITY_3_ARGS(CArithmetic, IsNearlyEqual) template FORCEINLINE constexpr bool IsNearlyZero(T A, T Epsilon = TNumericLimits::Epsilon()) { return Math::Abs(A) <= Epsilon; } RESOLVE_ARITHMETIC_AMBIGUITY_2_ARGS(CArithmetic, IsNearlyZero) template FORCEINLINE constexpr T IsInfinity(T A) { using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits; auto IntegralValue = Math::BitCast(A); return (IntegralValue & Traits::ExponentMask) == Traits::ExponentMask && (IntegralValue & Traits::MantissaMask) == 0; } template FORCEINLINE constexpr T IsNaN(T A) { using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits; auto IntegralValue = Math::BitCast(A); return (IntegralValue & Traits::ExponentMask) == Traits::ExponentMask && (IntegralValue & Traits::MantissaMask) != 0; } template FORCEINLINE constexpr T IsNormal(T A) { using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits; auto IntegralValue = Math::BitCast(A); return (IntegralValue & Traits::ExponentMask) != 0 && (IntegralValue & Traits::ExponentMask) != Traits::ExponentMask; } template FORCEINLINE constexpr T IsDenorm(T A) { using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits; auto IntegralValue = Math::BitCast(A); return (IntegralValue & Traits::ExponentMask) == 0 && (IntegralValue & Traits::MantissaMask) != 0; } template FORCEINLINE constexpr bool IsNegative(T A) { using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits; auto IntegralValue = Math::BitCast(A); return (IntegralValue & Traits::SignMask) >> Traits::SignShift; } template FORCEINLINE constexpr uint Exponent(T A) { using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits; auto IntegralValue = Math::BitCast(A); return ((IntegralValue & Traits::ExponentMask) >> Traits::ExponentShift) - Traits::ExponentBias; } template FORCEINLINE constexpr T NaN(U Payload) { using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits; checkf(Payload != 0, TEXT("Illegal payload. It must not be zero.")); checkf(Payload < (static_cast(1) << Traits::MantissaBits), TEXT("Illegal payload. It must be less than 2^MantissaBits.")); if (Payload == 0) return TNumericLimits::QuietNaN(); typename Traits::FIntegralT ValidPayload = Payload & Traits::MantissaMask; return Math::BitCast(ValidPayload | Traits::ExponentMask); } template FORCEINLINE constexpr T NaN(U Payload) { TUnderlyingType IntegralValue = static_cast>(Payload); return Math::NaN(IntegralValue); } template FORCEINLINE constexpr auto NaNPayload(T A) { using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits; auto IntegralValue = Math::BitCast(A); return IntegralValue & Traits::MantissaMask; } template FORCEINLINE constexpr auto NaNPayload(U A) { return static_cast(Math::NaNPayload(A)); } #undef RESOLVE_ARITHMETIC_AMBIGUITY_2_ARGS #undef RESOLVE_ARITHMETIC_AMBIGUITY_3_ARGS NAMESPACE_END(Math) NAMESPACE_MODULE_END(Utility) NAMESPACE_MODULE_END(Redcraft) NAMESPACE_REDCRAFT_END