#ifndef MATHTEST_NUMERICS_HPP
#define MATHTEST_NUMERICS_HPP
#include "mathtest/Support.hpp"
#include "mathtest/TypeExtras.hpp"
#include "shared/fp_bits.h"
#include "shared/sign.h"
#include <climits>
#include <cstdint>
#include <limits>
#include <math.h>
#include <type_traits>
namespace mathtest {
template <typename FloatType>
using FPBits = LIBC_NAMESPACE::shared::FPBits<FloatType>;
using Sign = LIBC_NAMESPACE::shared::Sign;
template <typename T> struct IsFloatingPoint : std::is_floating_point<T> {};
template <> struct IsFloatingPoint<float16> : std::true_type {};
template <typename T>
inline constexpr bool IsFloatingPoint_v
= IsFloatingPoint<T>::value;
template <typename T> struct StorageTypeOf {
private:
static constexpr auto getStorageType() noexcept {
if constexpr (IsFloatingPoint_v<T>)
return TypeIdentityOf<typename FPBits<T>::StorageType>{};
else if constexpr (std::is_unsigned_v<T>)
return TypeIdentityOf<T>{};
else if constexpr (std::is_signed_v<T>)
return TypeIdentityOf<std::make_unsigned_t<T>>{};
else
static_assert(!std::is_same_v<T, T>, "Unsupported type");
}
public:
using type = typename decltype(getStorageType())::type;
};
template <typename T> using StorageTypeOf_t = typename StorageTypeOf<T>::type;
template <typename T> [[nodiscard]] constexpr T getMinOrNegInf() noexcept {
if constexpr (IsFloatingPoint_v<T>) {
return FPBits<T>::inf(Sign::NEG).get_val();
} else {
static_assert(std::is_integral_v<T>,
"Type T must be an integral or floating-point type");
return std::numeric_limits<T>::lowest();
}
}
template <typename T> [[nodiscard]] constexpr T getMaxOrInf() noexcept {
if constexpr (IsFloatingPoint_v<T>) {
return FPBits<T>::inf(Sign::POS).get_val();
} else {
static_assert(std::is_integral_v<T>,
"Type T must be an integral or floating-point type");
return std::numeric_limits<T>::max();
}
}
template <typename FloatType>
[[nodiscard]] uint64_t computeUlpDistance(FloatType X, FloatType Y) noexcept {
static_assert(IsFloatingPoint_v<FloatType>,
"FloatType must be a floating-point type");
using FPBits = FPBits<FloatType>;
using StorageType = typename FPBits::StorageType;
const FPBits XBits(X);
const FPBits YBits(Y);
if (X == Y) {
if (XBits.sign() != YBits.sign()) [[unlikely]] {
return 1;
}
return 0;
}
const bool XIsNaN = XBits.is_nan();
const bool YIsNaN = YBits.is_nan();
if (XIsNaN && YIsNaN)
return 0;
if (XIsNaN || YIsNaN)
return std::numeric_limits<uint64_t>::max();
constexpr StorageType SignMask = FPBits::SIGN_MASK;
auto MapToOrderedUnsigned = [](FPBits Bits) {
const StorageType Unsigned = Bits.uintval();
return (Unsigned & SignMask) ? SignMask - (Unsigned - SignMask)
: SignMask + Unsigned;
};
const StorageType MappedX = MapToOrderedUnsigned(XBits);
const StorageType MappedY = MapToOrderedUnsigned(YBits);
return static_cast<uint64_t>(MappedX > MappedY ? MappedX - MappedY
: MappedY - MappedX);
}
}
#endif