Compare commits
4 Commits
f60bd0e3e4
...
f5c47fe677
Author | SHA1 | Date | |
---|---|---|---|
f5c47fe677 | |||
84a41387ae | |||
edc6c1924a | |||
5a5d34e908 |
176
Redcraft.Utility/Source/Private/Testing/NumericTesting.cpp
Normal file
176
Redcraft.Utility/Source/Private/Testing/NumericTesting.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
#include "Testing/NumericTesting.h"
|
||||
|
||||
#include "Numeric/Numeric.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
void TestNumeric()
|
||||
{
|
||||
TestLiteral();
|
||||
TestBit();
|
||||
TestMath();
|
||||
}
|
||||
|
||||
void TestLiteral()
|
||||
{
|
||||
// always_check((CSameAs<decltype(0i8), int8 >));
|
||||
always_check((CSameAs<decltype(0i16), int16>));
|
||||
always_check((CSameAs<decltype(0i32), int32>));
|
||||
always_check((CSameAs<decltype(0i64), int64>));
|
||||
|
||||
always_check((CSameAs<decltype(0u8), uint8 >));
|
||||
always_check((CSameAs<decltype(0u16), uint16>));
|
||||
always_check((CSameAs<decltype(0u32), uint32>));
|
||||
always_check((CSameAs<decltype(0u64), uint64>));
|
||||
|
||||
always_check((CSameAs<decltype(0imax), intmax>));
|
||||
always_check((CSameAs<decltype(0umax), uintmax>));
|
||||
|
||||
always_check((CSameAs<decltype(0.0f32), float32>));
|
||||
always_check((CSameAs<decltype(0.0f64), float64>));
|
||||
}
|
||||
|
||||
void TestBit()
|
||||
{
|
||||
always_check(Math::ByteSwap<uint8 >(0x00 ) == 0x00 );
|
||||
always_check(Math::ByteSwap<uint16>(0x0011 ) == 0x1100 );
|
||||
always_check(Math::ByteSwap<uint32>(0x00112233 ) == 0x33221100 );
|
||||
always_check(Math::ByteSwap<uint64>(0x0011223344556677) == 0x7766554433221100);
|
||||
|
||||
always_check(Math::IsSingleBit(0b0000u) == false);
|
||||
always_check(Math::IsSingleBit(0b0001u) == true );
|
||||
always_check(Math::IsSingleBit(0b0010u) == true );
|
||||
always_check(Math::IsSingleBit(0b0011u) == false);
|
||||
always_check(Math::IsSingleBit(0b0100u) == true );
|
||||
always_check(Math::IsSingleBit(0b0101u) == false);
|
||||
always_check(Math::IsSingleBit(0b0110u) == false);
|
||||
always_check(Math::IsSingleBit(0b0111u) == false);
|
||||
always_check(Math::IsSingleBit(0b1000u) == true );
|
||||
always_check(Math::IsSingleBit(0b1001u) == false);
|
||||
|
||||
always_check(Math::CountAllZero(0b00000000u8) == 8);
|
||||
always_check(Math::CountAllZero(0b11111111u8) == 0);
|
||||
always_check(Math::CountAllZero(0b00011101u8) == 4);
|
||||
|
||||
always_check(Math::CountAllOne(0b00000000u8) == 0);
|
||||
always_check(Math::CountAllOne(0b11111111u8) == 8);
|
||||
always_check(Math::CountAllOne(0b00011101u8) == 4);
|
||||
|
||||
always_check(Math::CountLeftZero(0b00000000u8) == 8);
|
||||
always_check(Math::CountLeftZero(0b11111111u8) == 0);
|
||||
always_check(Math::CountLeftZero(0b00011100u8) == 3);
|
||||
|
||||
always_check(Math::CountLeftOne(0b00000000u8) == 0);
|
||||
always_check(Math::CountLeftOne(0b11111111u8) == 8);
|
||||
always_check(Math::CountLeftOne(0b11100011u8) == 3);
|
||||
|
||||
always_check(Math::CountRightZero(0b00000000u8) == 8);
|
||||
always_check(Math::CountRightZero(0b11111111u8) == 0);
|
||||
always_check(Math::CountRightZero(0b00011100u8) == 2);
|
||||
|
||||
always_check(Math::CountRightOne(0b00000000u8) == 0);
|
||||
always_check(Math::CountRightOne(0b11111111u8) == 8);
|
||||
always_check(Math::CountRightOne(0b11100011u8) == 2);
|
||||
|
||||
always_check(Math::BitWidth(0b0000u) == 0);
|
||||
always_check(Math::BitWidth(0b0001u) == 1);
|
||||
always_check(Math::BitWidth(0b0010u) == 2);
|
||||
always_check(Math::BitWidth(0b0011u) == 2);
|
||||
always_check(Math::BitWidth(0b0100u) == 3);
|
||||
always_check(Math::BitWidth(0b0101u) == 3);
|
||||
always_check(Math::BitWidth(0b0110u) == 3);
|
||||
always_check(Math::BitWidth(0b0111u) == 3);
|
||||
|
||||
always_check(Math::BitCeil(0b00000000u) == 0b00000001u);
|
||||
always_check(Math::BitCeil(0b00000001u) == 0b00000001u);
|
||||
always_check(Math::BitCeil(0b00000010u) == 0b00000010u);
|
||||
always_check(Math::BitCeil(0b00000011u) == 0b00000100u);
|
||||
always_check(Math::BitCeil(0b00000100u) == 0b00000100u);
|
||||
always_check(Math::BitCeil(0b00000101u) == 0b00001000u);
|
||||
always_check(Math::BitCeil(0b00000110u) == 0b00001000u);
|
||||
always_check(Math::BitCeil(0b00000111u) == 0b00001000u);
|
||||
always_check(Math::BitCeil(0b00001000u) == 0b00001000u);
|
||||
always_check(Math::BitCeil(0b00001001u) == 0b00010000u);
|
||||
|
||||
always_check(Math::BitFloor(0b00000000u) == 0b00000000u);
|
||||
always_check(Math::BitFloor(0b00000001u) == 0b00000001u);
|
||||
always_check(Math::BitFloor(0b00000010u) == 0b00000010u);
|
||||
always_check(Math::BitFloor(0b00000011u) == 0b00000010u);
|
||||
always_check(Math::BitFloor(0b00000100u) == 0b00000100u);
|
||||
always_check(Math::BitFloor(0b00000101u) == 0b00000100u);
|
||||
always_check(Math::BitFloor(0b00000110u) == 0b00000100u);
|
||||
always_check(Math::BitFloor(0b00000111u) == 0b00000100u);
|
||||
always_check(Math::BitFloor(0b00001000u) == 0b00001000u);
|
||||
always_check(Math::BitFloor(0b00001001u) == 0b00001000u);
|
||||
|
||||
always_check(Math::RotateLeft(0b00011101u8, 0) == 0b00011101u8);
|
||||
always_check(Math::RotateLeft(0b00011101u8, 1) == 0b00111010u8);
|
||||
always_check(Math::RotateLeft(0b00011101u8, 4) == 0b11010001u8);
|
||||
always_check(Math::RotateLeft(0b00011101u8, 9) == 0b00111010u8);
|
||||
always_check(Math::RotateLeft(0b00011101u8, -1) == 0b10001110u8);
|
||||
|
||||
always_check(Math::RotateRight(0b00011101u8, 0) == 0b00011101u8);
|
||||
always_check(Math::RotateRight(0b00011101u8, 1) == 0b10001110u8);
|
||||
always_check(Math::RotateRight(0b00011101u8, 4) == 0b11010001u8);
|
||||
always_check(Math::RotateRight(0b00011101u8, 9) == 0b10001110u8);
|
||||
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_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "AssertionMacros.h"
|
||||
#include "CoreTypes.h"
|
||||
|
||||
#include <cstdlib>
|
||||
@ -84,6 +85,10 @@ NORETURN FORCEINLINE void Unreachable()
|
||||
{
|
||||
NAMESPACE_STD::unreachable();
|
||||
}
|
||||
# else
|
||||
{
|
||||
Abort();
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
|
228
Redcraft.Utility/Source/Public/Numeric/Bit.h
Normal file
228
Redcraft.Utility/Source/Public/Numeric/Bit.h
Normal file
@ -0,0 +1,228 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Numeric/Limits.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
#include <bit>
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Math)
|
||||
|
||||
template <CTriviallyCopyable T, CTriviallyCopyable U> requires (sizeof(T) == sizeof(U))
|
||||
FORCEINLINE constexpr T BitCast(const U& Value)
|
||||
{
|
||||
return __builtin_bit_cast(T, Value);
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr T ByteSwap(T Value)
|
||||
{
|
||||
static_assert(sizeof(T) <= 16, "ByteSwap only works with T up to 128 bits");
|
||||
|
||||
if constexpr (sizeof(T) == 1) return Value;
|
||||
|
||||
# if PLATFORM_COMPILER_MSVC
|
||||
{
|
||||
if constexpr (sizeof(T) == 2) return _byteswap_ushort(Value);
|
||||
if constexpr (sizeof(T) == 4) return _byteswap_ulong(Value);
|
||||
if constexpr (sizeof(T) == 8) return _byteswap_uint64(Value);
|
||||
}
|
||||
# elif PLATFORM_COMPILER_CLANG || PLATFORM_COMPILER_GCC
|
||||
{
|
||||
if constexpr (sizeof(T) == 2) return __builtin_bswap16(Value);
|
||||
if constexpr (sizeof(T) == 4) return __builtin_bswap32(Value);
|
||||
if constexpr (sizeof(T) == 8) return __builtin_bswap64(Value);
|
||||
}
|
||||
# else
|
||||
{
|
||||
if constexpr (sizeof(T) == 2) return (Value << 8) | (Value >> 8); // AB -> BA
|
||||
|
||||
if constexpr (sizeof(T) == 4)
|
||||
{
|
||||
T Result = 0;
|
||||
|
||||
Result = ((Result << 8) & 0xFF00FF00u32) | ((Result >> 8) & 0x00FF00FFu32); // ABCD -> BADC
|
||||
|
||||
return (Result << 16) | (Result >> 16);
|
||||
}
|
||||
|
||||
if constexpr (sizeof(T) == 8)
|
||||
{
|
||||
T Result = Value;
|
||||
|
||||
Result = ((Result << 8) & 0xFF00FF00FF00FF00u64) | ((Result >> 8) & 0x00FF00FF00FF00FFu64); // ABCDEFGH -> BADCFEHG
|
||||
Result = ((Result << 16) & 0xFFFF0000FFFF0000u64) | ((Result >> 16) & 0x0000FFFF0000FFFFu64); // BADCFEHG -> DCBAHGFE
|
||||
|
||||
return (Result << 32) | (Result >> 32);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
# if PLATFORM_HAS_INT128
|
||||
{
|
||||
if constexpr (sizeof(T) == 16)
|
||||
{
|
||||
T Result = Value;
|
||||
|
||||
Result = ((Result << 8) & 0xFF00FF00FF00FF00FF00FF00FF00FF00u128) | ((Result >> 8) & 0x00FF00FF00FF00FF00FF00FF00FF00FFu128); // ABCDEFGHIJKLMNOP -> BADCFEHGJILKMONP
|
||||
Result = ((Result << 16) & 0xFFFF0000FFFF0000FFFF0000FFFF0000u128) | ((Result >> 16) & 0x0000FFFF0000FFFF0000FFFF0000FFFFu128); // BADCFEHGJILKMONP -> DCBAHGFEJIKLNOPM
|
||||
Result = ((Result << 32) & 0xFFFFFFFF00000000FFFFFFFF00000000u128) | ((Result >> 32) & 0x00000000FFFFFFFF00000000FFFFFFFFu128); // DCBAHGFEJIKLNOPM -> HGFEDCBAKJILMOPN
|
||||
|
||||
return (Result << 64) | (Result >> 64);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr bool IsSingleBit(T Value)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value;
|
||||
|
||||
else return Value && !(Value & (Value - 1));
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr uint CountAllZero(T Value)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value ? 0 : 1;
|
||||
|
||||
else return static_cast<uint>(TNumericLimits<T>::Digits - NAMESPACE_STD::popcount(Value));
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr uint CountAllOne(T Value)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value ? 1 : 0;
|
||||
|
||||
else return static_cast<uint>(NAMESPACE_STD::popcount(Value));
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr uint CountLeftZero(T Value)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value ? 0 : 1;
|
||||
|
||||
else return static_cast<uint>(NAMESPACE_STD::countl_zero(Value));
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr uint CountLeftOne(T Value)
|
||||
{
|
||||
return Math::CountLeftZero<T>(~Value);
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr uint CountRightZero(T Value)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value ? 0 : 1;
|
||||
|
||||
else return static_cast<uint>(NAMESPACE_STD::countr_zero(Value));
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr uint CountRightOne(T Value)
|
||||
{
|
||||
return Math::CountRightZero<T>(~Value);
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr T BitWidth(T Value)
|
||||
{
|
||||
return TNumericLimits<T>::Digits - Math::CountLeftZero(Value);
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr T BitCeil(T Value)
|
||||
{
|
||||
if (Value <= 1u) return static_cast<T>(1);
|
||||
|
||||
return static_cast<T>(1) << Math::BitWidth(static_cast<T>(Value - 1));
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr T BitFloor(T Value)
|
||||
{
|
||||
if (Value == 0u) return static_cast<T>(0);
|
||||
|
||||
return static_cast<T>(1) << (Math::BitWidth(static_cast<T>(Value)) - 1);
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T> FORCEINLINE constexpr T RotateLeft (T Value, int Offset);
|
||||
template <CUnsignedIntegral T> FORCEINLINE constexpr T RotateRight(T Value, int Offset);
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr T RotateLeft(T Value, int Offset)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value;
|
||||
|
||||
else
|
||||
{
|
||||
if (Offset >= 0)
|
||||
{
|
||||
const auto Remainder = Offset % TNumericLimits<T>::Digits;
|
||||
|
||||
return static_cast<T>((Value << Remainder) | (Value >> (TNumericLimits<T>::Digits - Remainder)));
|
||||
}
|
||||
|
||||
return Math::RotateRight(Value, -Offset);
|
||||
}
|
||||
}
|
||||
|
||||
template <CUnsignedIntegral T>
|
||||
FORCEINLINE constexpr T RotateRight(T Value, int Offset)
|
||||
{
|
||||
if constexpr (CSameAs<T, bool>) return Value;
|
||||
|
||||
else
|
||||
{
|
||||
if (Offset >= 0)
|
||||
{
|
||||
const auto Remainder = Offset % TNumericLimits<T>::Digits;
|
||||
|
||||
return static_cast<T>((Value >> Remainder) | (Value << (TNumericLimits<T>::Digits - Remainder)));
|
||||
}
|
||||
return Math::RotateLeft(Value, -Offset);
|
||||
}
|
||||
}
|
||||
|
||||
enum class EEndian
|
||||
{
|
||||
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__)
|
||||
|
||||
Little = __ORDER_LITTLE_ENDIAN__,
|
||||
Big = __ORDER_BIG_ENDIAN__,
|
||||
Native = __BYTE_ORDER__,
|
||||
|
||||
#elif PLATFORM_LITTLE_ENDIAN
|
||||
|
||||
Little,
|
||||
Big,
|
||||
Native = Little,
|
||||
|
||||
#elif PLATFORM_BIG_ENDIAN
|
||||
|
||||
Little,
|
||||
Big,
|
||||
Native = Big,
|
||||
|
||||
#else
|
||||
|
||||
Little,
|
||||
Big,
|
||||
Native,
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
NAMESPACE_END(Math)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
@ -3,6 +3,7 @@
|
||||
#include "CoreTypes.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
|
||||
#include <climits>
|
||||
#include <limits>
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
133
Redcraft.Utility/Source/Public/Numeric/Literal.h
Normal file
133
Redcraft.Utility/Source/Public/Numeric/Literal.h
Normal file
@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
#if PLATFORM_COMPILER_MSVC
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4455)
|
||||
#elif PLATFORM_COMPILER_GCC || PLATFORM_COMPILER_CLANG
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wliteral-suffix"
|
||||
#endif
|
||||
|
||||
FORCEINLINE constexpr int8 operator""i8 (unsigned long long int Value) { return static_cast<int8 >(Value); }
|
||||
FORCEINLINE constexpr int16 operator""i16(unsigned long long int Value) { return static_cast<int16>(Value); }
|
||||
FORCEINLINE constexpr int32 operator""i32(unsigned long long int Value) { return static_cast<int32>(Value); }
|
||||
FORCEINLINE constexpr int64 operator""i64(unsigned long long int Value) { return static_cast<int64>(Value); }
|
||||
|
||||
FORCEINLINE constexpr int8 operator""I8 (unsigned long long int Value) { return static_cast<int8 >(Value); }
|
||||
FORCEINLINE constexpr int16 operator""I16(unsigned long long int Value) { return static_cast<int16>(Value); }
|
||||
FORCEINLINE constexpr int32 operator""I32(unsigned long long int Value) { return static_cast<int32>(Value); }
|
||||
FORCEINLINE constexpr int64 operator""I64(unsigned long long int Value) { return static_cast<int64>(Value); }
|
||||
|
||||
FORCEINLINE constexpr uint8 operator""u8 (unsigned long long int Value) { return static_cast<uint8 >(Value); }
|
||||
FORCEINLINE constexpr uint16 operator""u16(unsigned long long int Value) { return static_cast<uint16>(Value); }
|
||||
FORCEINLINE constexpr uint32 operator""u32(unsigned long long int Value) { return static_cast<uint32>(Value); }
|
||||
FORCEINLINE constexpr uint64 operator""u64(unsigned long long int Value) { return static_cast<uint64>(Value); }
|
||||
|
||||
FORCEINLINE constexpr uint8 operator""U8 (unsigned long long int Value) { return static_cast<uint8 >(Value); }
|
||||
FORCEINLINE constexpr uint16 operator""U16(unsigned long long int Value) { return static_cast<uint16>(Value); }
|
||||
FORCEINLINE constexpr uint32 operator""U32(unsigned long long int Value) { return static_cast<uint32>(Value); }
|
||||
FORCEINLINE constexpr uint64 operator""U64(unsigned long long int Value) { return static_cast<uint64>(Value); }
|
||||
|
||||
#if PLATFORM_HAS_INT128
|
||||
|
||||
FORCEINLINE constexpr int128 operator""i128(const char* Str);
|
||||
FORCEINLINE constexpr uint128 operator""u128(const char* Str);
|
||||
|
||||
FORCEINLINE constexpr int128 operator""I128(const char* Str) { return operator""i128(Str); }
|
||||
FORCEINLINE constexpr uint128 operator""U128(const char* Str) { return operator""u128(Str); }
|
||||
|
||||
FORCEINLINE constexpr int128 operator""i128(const char* Str)
|
||||
{
|
||||
return static_cast<int128>(operator""u128(Str));
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr uint128 operator""u128(const char* Str)
|
||||
{
|
||||
constexpr uint8 DigitFromChar[] =
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
uint128 Result = 0;
|
||||
|
||||
uint Base = 10;
|
||||
|
||||
size_t BeginIndex = 0;
|
||||
|
||||
if (Str[0] == '0')
|
||||
{
|
||||
if (Str[1] == 'x' || Str[1] == 'X')
|
||||
{
|
||||
Base = 16;
|
||||
BeginIndex += 2;
|
||||
}
|
||||
else if (Str[1] == 'b' || Str[1] == 'B')
|
||||
{
|
||||
Base = 2;
|
||||
BeginIndex += 2;
|
||||
}
|
||||
else Base = 8;
|
||||
}
|
||||
|
||||
for (size_t I = BeginIndex; Str[I]; ++I)
|
||||
{
|
||||
Result = Result * Base + DigitFromChar[Str[I]];
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if PLATFORM_HAS_INT128
|
||||
FORCEINLINE constexpr intmax operator""imax(const char* Str) { return operator""i128(Str); }
|
||||
FORCEINLINE constexpr intmax operator""IMAX(const char* Str) { return operator""I128(Str); }
|
||||
FORCEINLINE constexpr uintmax operator""umax(const char* Str) { return operator""u128(Str); }
|
||||
FORCEINLINE constexpr uintmax operator""UMAX(const char* Str) { return operator""U128(Str); }
|
||||
#else
|
||||
FORCEINLINE constexpr intmax operator""imax(unsigned long long int Value) { return operator""i64(Value); }
|
||||
FORCEINLINE constexpr intmax operator""IMAX(unsigned long long int Value) { return operator""I64(Value); }
|
||||
FORCEINLINE constexpr uintmax operator""umax(unsigned long long int Value) { return operator""u64(Value); }
|
||||
FORCEINLINE constexpr uintmax operator""UMAX(unsigned long long int Value) { return operator""U64(Value); }
|
||||
#endif
|
||||
|
||||
#ifndef __STDCPP_FLOAT32_T__
|
||||
FORCEINLINE constexpr float32 operator""f32(long double Value) { return static_cast<float32>(Value); }
|
||||
FORCEINLINE constexpr float32 operator""F32(long double Value) { return static_cast<float32>(Value); }
|
||||
#endif
|
||||
|
||||
#ifndef __STDCPP_FLOAT64_T__
|
||||
FORCEINLINE constexpr float64 operator""f64(long double Value) { return static_cast<float64>(Value); }
|
||||
FORCEINLINE constexpr float64 operator""F64(long double Value) { return static_cast<float64>(Value); }
|
||||
#endif
|
||||
|
||||
#if PLATFORM_COMPILER_MSVC
|
||||
# pragma warning(pop)
|
||||
#elif PLATFORM_COMPILER_GCC || PLATFORM_COMPILER_CLANG
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
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
|
@ -10,7 +10,7 @@ NAMESPACE_MODULE_BEGIN(Utility)
|
||||
NAMESPACE_BEGIN(Math)
|
||||
|
||||
template <CFloatingPoint T>
|
||||
struct TConstant
|
||||
struct TNumbers
|
||||
{
|
||||
static constexpr T E = static_cast<T>(2.71828182845904523536028747135266249775724709369995);
|
||||
static constexpr T Log2E = static_cast<T>(1.44269504088896340735992468100189213742664595415299);
|
8
Redcraft.Utility/Source/Public/Numeric/Numeric.h
Normal file
8
Redcraft.Utility/Source/Public/Numeric/Numeric.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Numeric/Literal.h"
|
||||
#include "Numeric/Limits.h"
|
||||
#include "Numeric/Numbers.h"
|
||||
#include "Numeric/Bit.h"
|
||||
#include "Numeric/Math.h"
|
20
Redcraft.Utility/Source/Public/Testing/NumericTesting.h
Normal file
20
Redcraft.Utility/Source/Public/Testing/NumericTesting.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_BEGIN(Testing)
|
||||
|
||||
REDCRAFTUTILITY_API void TestNumeric();
|
||||
REDCRAFTUTILITY_API void TestLiteral();
|
||||
REDCRAFTUTILITY_API void TestBit();
|
||||
REDCRAFTUTILITY_API void TestMath();
|
||||
|
||||
NAMESPACE_END(Testing)
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
Loading…
Reference in New Issue
Block a user