feat(numeric): add basic and floating point manipulation functions and the corresponding testing
This commit is contained in:
		| @@ -13,6 +13,7 @@ void TestNumeric() | |||||||
| { | { | ||||||
| 	TestLiteral(); | 	TestLiteral(); | ||||||
| 	TestBit(); | 	TestBit(); | ||||||
|  | 	TestMath(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void TestLiteral() | void TestLiteral() | ||||||
| @@ -120,6 +121,54 @@ void TestBit() | |||||||
| 	always_check(Math::RotateRight(0b00011101u8, -1) == 0b00111010u8); | 	always_check(Math::RotateRight(0b00011101u8, -1) == 0b00111010u8); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void TestMath() | ||||||
|  | { | ||||||
|  | 	always_check(Math::Abs(-1) == 1); | ||||||
|  | 	always_check(Math::Abs( 0) == 0); | ||||||
|  | 	always_check(Math::Abs( 1) == 1); | ||||||
|  |  | ||||||
|  | 	always_check(Math::Sign(-4) == -1); | ||||||
|  | 	always_check(Math::Sign( 0) ==  0); | ||||||
|  | 	always_check(Math::Sign( 4) ==  1); | ||||||
|  |  | ||||||
|  | 	always_check(Math::Min(1, 2, 3, 4, 5) == 1); | ||||||
|  | 	always_check(Math::Min(5, 4, 3, 2, 1) == 1); | ||||||
|  | 	always_check(Math::Max(1, 2, 3, 4, 5) == 5); | ||||||
|  | 	always_check(Math::Max(5, 4, 3, 2, 1) == 5); | ||||||
|  |  | ||||||
|  | 	always_check(Math::IsNearlyEqual(4.0, 4.0)); | ||||||
|  |  | ||||||
|  | 	always_check(Math::IsNearlyZero(0.0)); | ||||||
|  |  | ||||||
|  | 	always_check(Math::IsInfinity( TNumericLimits<float32>::Infinity())); | ||||||
|  | 	always_check(Math::IsInfinity(-TNumericLimits<float32>::Infinity())); | ||||||
|  |  | ||||||
|  | 	always_check(Math::IsNaN( TNumericLimits<float32>::QuietNaN())); | ||||||
|  | 	always_check(Math::IsNaN(-TNumericLimits<float32>::QuietNaN())); | ||||||
|  | 	always_check(Math::IsNaN( TNumericLimits<float32>::SignalingNaN())); | ||||||
|  | 	always_check(Math::IsNaN(-TNumericLimits<float32>::SignalingNaN())); | ||||||
|  |  | ||||||
|  | 	always_check(Math::IsNaN(Math::NaN<float32>(4u))); | ||||||
|  |  | ||||||
|  | 	always_check(Math::IsNormal(1.0e4)); | ||||||
|  | 	always_check(Math::IsNormal(1.0e8)); | ||||||
|  |  | ||||||
|  | 	always_check(!Math::IsNegative(+1.0)); | ||||||
|  | 	always_check(!Math::IsNegative(+0.0)); | ||||||
|  | 	always_check( Math::IsNegative(-0.0)); | ||||||
|  | 	always_check( Math::IsNegative(-1.0)); | ||||||
|  |  | ||||||
|  | 	always_check(Math::Exponent(1.0) == 0); | ||||||
|  | 	always_check(Math::Exponent(2.0) == 1); | ||||||
|  | 	always_check(Math::Exponent(4.0) == 2); | ||||||
|  |  | ||||||
|  | 	always_check(Math::NaNPayload(Math::NaN<float32>(4u)) == 4u); | ||||||
|  |  | ||||||
|  | 	enum class ETest : uint16 { A = 65535 }; | ||||||
|  |  | ||||||
|  | 	always_check(Math::NaNPayload<ETest>(Math::NaN<float32>(ETest::A)) == ETest::A); | ||||||
|  | } | ||||||
|  |  | ||||||
| NAMESPACE_END(Testing) | NAMESPACE_END(Testing) | ||||||
|  |  | ||||||
| NAMESPACE_MODULE_END(Utility) | NAMESPACE_MODULE_END(Utility) | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ FORCEINLINE constexpr T BitCast(const U& Value) | |||||||
| template <CUnsignedIntegral T> | template <CUnsignedIntegral T> | ||||||
| FORCEINLINE constexpr T ByteSwap(T Value) | FORCEINLINE constexpr T ByteSwap(T Value) | ||||||
| { | { | ||||||
| 	static_assert(sizeof(T) <= 16, "ByteSwap only works with T up to 128 bits."); | 	static_assert(sizeof(T) <= 16, "ByteSwap only works with T up to 128 bits"); | ||||||
|  |  | ||||||
| 	if constexpr (sizeof(T) == 1) return Value; | 	if constexpr (sizeof(T) == 1) return Value; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										280
									
								
								Redcraft.Utility/Source/Public/Numeric/Math.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								Redcraft.Utility/Source/Public/Numeric/Math.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | |||||||
|  | #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 <CFloatingPoint T> | ||||||
|  | struct TFloatingTypeTraits | ||||||
|  | { | ||||||
|  | 	static_assert(sizeof(T) == -1, "Unsupported floating point type."); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct TFloatingTypeTraits<float> | ||||||
|  | { | ||||||
|  | 	// 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<double> | ||||||
|  | { | ||||||
|  | 	// 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 <Concept T, Concept U> requires (CCommonType<T, U>) \ | ||||||
|  | 	FORCEINLINE constexpr auto Func(T A, U B)                    \ | ||||||
|  | 	{                                                            \ | ||||||
|  | 		return Math::Func<TCommonType<T, U>>(A, B);              \ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | #define RESOLVE_ARITHMETIC_AMBIGUITY_3_ARGS(Concept, Func)                     \ | ||||||
|  | 	template <Concept T, Concept U, Concept V> requires (CCommonType<T, U, V>) \ | ||||||
|  | 	FORCEINLINE constexpr auto Func(T A, U B, V C)                             \ | ||||||
|  | 	{                                                                          \ | ||||||
|  | 		return Math::Func<TCommonType<T, U, V>>(A, B, C);                      \ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | template <CSigned T> | ||||||
|  | FORCEINLINE constexpr T Abs(T A) | ||||||
|  | { | ||||||
|  | 	return A < 0 ? -A : A; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CUnsigned T> | ||||||
|  | FORCEINLINE constexpr T Abs(T A) | ||||||
|  | { | ||||||
|  | 	return A; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CArithmetic T> | ||||||
|  | FORCEINLINE constexpr T Sign(T A) | ||||||
|  | { | ||||||
|  | 	if (A == static_cast<T>(0)) return static_cast<T>( 0); | ||||||
|  | 	if (A <  static_cast<T>(0)) return static_cast<T>(-1); | ||||||
|  |  | ||||||
|  | 	return static_cast<T>(1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CArithmetic T, CArithmetic... Ts> requires (CCommonType<T, Ts...>) | ||||||
|  | FORCEINLINE constexpr auto Min(T A, Ts... InOther) | ||||||
|  | { | ||||||
|  | 	if constexpr (sizeof...(Ts) == 0) return A; | ||||||
|  |  | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		using FCommonT = TCommonType<T, Ts...>; | ||||||
|  |  | ||||||
|  | 		FCommonT B = Math::Min(InOther...); | ||||||
|  |  | ||||||
|  | 		return A < B ? A : B; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CArithmetic T, CArithmetic... Ts> requires (CCommonType<T, Ts...>) | ||||||
|  | FORCEINLINE constexpr auto Max(T A, Ts... InOther) | ||||||
|  | { | ||||||
|  | 	if constexpr (sizeof...(Ts) == 0) return A; | ||||||
|  |  | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		using FCommonT = TCommonType<T, Ts...>; | ||||||
|  |  | ||||||
|  | 		FCommonT B = Math::Max(InOther...); | ||||||
|  |  | ||||||
|  | 		return A > B ? A : B; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CIntegral T> | ||||||
|  | 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 <CArithmetic T> | ||||||
|  | FORCEINLINE constexpr bool IsNearlyEqual(T LHS, T RHS, T Epsilon = TNumericLimits<T>::Epsilon()) | ||||||
|  | { | ||||||
|  | 	return Math::Abs<T>(LHS - RHS) <= Epsilon; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | RESOLVE_ARITHMETIC_AMBIGUITY_2_ARGS(CArithmetic, IsNearlyEqual) | ||||||
|  | RESOLVE_ARITHMETIC_AMBIGUITY_3_ARGS(CArithmetic, IsNearlyEqual) | ||||||
|  |  | ||||||
|  | template <CArithmetic T> | ||||||
|  | FORCEINLINE constexpr bool IsNearlyZero(T A, T Epsilon = TNumericLimits<T>::Epsilon()) | ||||||
|  | { | ||||||
|  | 	return Math::Abs<T>(A) <= Epsilon; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | RESOLVE_ARITHMETIC_AMBIGUITY_2_ARGS(CArithmetic, IsNearlyZero) | ||||||
|  |  | ||||||
|  | template <CFloatingPoint T> | ||||||
|  | FORCEINLINE constexpr T IsInfinity(T A) | ||||||
|  | { | ||||||
|  | 	using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits<T>; | ||||||
|  |  | ||||||
|  | 	auto IntegralValue = Math::BitCast<typename Traits::FIntegralT>(A); | ||||||
|  |  | ||||||
|  | 	return (IntegralValue & Traits::ExponentMask) == Traits::ExponentMask && (IntegralValue & Traits::MantissaMask) == 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CFloatingPoint T> | ||||||
|  | FORCEINLINE constexpr T IsNaN(T A) | ||||||
|  | { | ||||||
|  | 	using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits<T>; | ||||||
|  |  | ||||||
|  | 	auto IntegralValue = Math::BitCast<typename Traits::FIntegralT>(A); | ||||||
|  |  | ||||||
|  | 	return (IntegralValue & Traits::ExponentMask) == Traits::ExponentMask && (IntegralValue & Traits::MantissaMask) != 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CFloatingPoint T> | ||||||
|  | FORCEINLINE constexpr T IsNormal(T A) | ||||||
|  | { | ||||||
|  | 	using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits<T>; | ||||||
|  |  | ||||||
|  | 	auto IntegralValue = Math::BitCast<typename Traits::FIntegralT>(A); | ||||||
|  |  | ||||||
|  | 	return (IntegralValue & Traits::ExponentMask) != 0 && (IntegralValue & Traits::ExponentMask) != Traits::ExponentMask; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CFloatingPoint T> | ||||||
|  | FORCEINLINE constexpr T IsDenorm(T A) | ||||||
|  | { | ||||||
|  | 	using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits<T>; | ||||||
|  |  | ||||||
|  | 	auto IntegralValue = Math::BitCast<typename Traits::FIntegralT>(A); | ||||||
|  |  | ||||||
|  | 	return (IntegralValue & Traits::ExponentMask) == 0 && (IntegralValue & Traits::MantissaMask) != 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CFloatingPoint T> | ||||||
|  | FORCEINLINE constexpr bool IsNegative(T A) | ||||||
|  | { | ||||||
|  | 	using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits<T>; | ||||||
|  |  | ||||||
|  | 	auto IntegralValue = Math::BitCast<typename Traits::FIntegralT>(A); | ||||||
|  |  | ||||||
|  | 	return (IntegralValue & Traits::SignMask) >> Traits::SignShift; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CFloatingPoint T> | ||||||
|  | FORCEINLINE constexpr uint Exponent(T A) | ||||||
|  | { | ||||||
|  | 	using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits<T>; | ||||||
|  |  | ||||||
|  | 	auto IntegralValue = Math::BitCast<typename Traits::FIntegralT>(A); | ||||||
|  |  | ||||||
|  | 	return ((IntegralValue & Traits::ExponentMask) >> Traits::ExponentShift) - Traits::ExponentBias; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CFloatingPoint T, CUnsignedIntegral U> | ||||||
|  | FORCEINLINE constexpr T NaN(U Payload) | ||||||
|  | { | ||||||
|  | 	using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits<T>; | ||||||
|  |  | ||||||
|  | 	checkf(Payload != 0, TEXT("Illegal payload. It must not be zero.")); | ||||||
|  |  | ||||||
|  | 	checkf(Payload < (static_cast<typename Traits::FIntegralT>(1) << Traits::MantissaBits), TEXT("Illegal payload. It must be less than 2^MantissaBits.")); | ||||||
|  |  | ||||||
|  | 	if (Payload == 0) return TNumericLimits<T>::QuietNaN(); | ||||||
|  |  | ||||||
|  | 	typename Traits::FIntegralT ValidPayload = Payload & Traits::MantissaMask; | ||||||
|  |  | ||||||
|  | 	return Math::BitCast<T>(ValidPayload | Traits::ExponentMask); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CFloatingPoint T, CEnum U> | ||||||
|  | FORCEINLINE constexpr T NaN(U Payload) | ||||||
|  | { | ||||||
|  | 	TUnderlyingType<U> IntegralValue = static_cast<TUnderlyingType<U>>(Payload); | ||||||
|  |  | ||||||
|  | 	return Math::NaN<T>(IntegralValue); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CFloatingPoint T> | ||||||
|  | FORCEINLINE constexpr auto NaNPayload(T A) | ||||||
|  | { | ||||||
|  | 	using Traits = NAMESPACE_PRIVATE::TFloatingTypeTraits<T>; | ||||||
|  |  | ||||||
|  | 	auto IntegralValue = Math::BitCast<typename Traits::FIntegralT>(A); | ||||||
|  |  | ||||||
|  | 	return IntegralValue & Traits::MantissaMask; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <CEnum T, CFloatingPoint U> | ||||||
|  | FORCEINLINE constexpr auto NaNPayload(U A) | ||||||
|  | { | ||||||
|  | 	return static_cast<T>(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 | ||||||
| @@ -5,3 +5,4 @@ | |||||||
| #include "Numeric/Limits.h" | #include "Numeric/Limits.h" | ||||||
| #include "Numeric/Numbers.h" | #include "Numeric/Numbers.h" | ||||||
| #include "Numeric/Bit.h" | #include "Numeric/Bit.h" | ||||||
|  | #include "Numeric/Math.h" | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ NAMESPACE_BEGIN(Testing) | |||||||
| REDCRAFTUTILITY_API void TestNumeric(); | REDCRAFTUTILITY_API void TestNumeric(); | ||||||
| REDCRAFTUTILITY_API void TestLiteral(); | REDCRAFTUTILITY_API void TestLiteral(); | ||||||
| REDCRAFTUTILITY_API void TestBit(); | REDCRAFTUTILITY_API void TestBit(); | ||||||
|  | REDCRAFTUTILITY_API void TestMath(); | ||||||
|  |  | ||||||
| NAMESPACE_END(Testing) | NAMESPACE_END(Testing) | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user