Files
AnyPS5/core/libs/tests/GuestIcuNumeric.cpp
T

142 lines
8.8 KiB
C++

#include "prx/libSceIcu/libSceIcu.hpp"
#include <array>
#include <bit>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <limits>
#include <stdexcept>
namespace {
constexpr std::uint64_t NaNBits = 0x7ff8000000000000;
constexpr std::uint64_t InfinityBits = 0x7ff0000000000000;
constexpr std::uint64_t NegativeZeroBits = 0x8000000000000000;
void Require(bool condition, const char* message) {
if (condition) return;
std::fprintf(stderr, "ICU numeric: %s\n", message);
std::abort();
}
double FromBits(std::uint64_t bits) {
return std::bit_cast<double>(bits);
}
std::uint64_t Bits(double value) {
return std::bit_cast<std::uint64_t>(value);
}
template<class F>
bool Rejects(F call) {
try {
call();
} catch (const std::invalid_argument&) {
return true;
}
return false;
}
void Classification() {
const double infinity = std::numeric_limits<double>::infinity();
for (const std::uint64_t bits : std::array<std::uint64_t, 5>{NaNBits, 0x7ff0000000000001ull, 0x7fffffffffffffffull, 0xfff8000000000000ull, 0xfff0000000000001ull}) {
Require(uprv_isNaN_nid_postfix(FromBits(bits)) == 1, "NaN patterns are NaN");
Require(uprv_isInfinite_nid_postfix(FromBits(bits)) == 0, "NaN patterns are not infinite");
Require(uprv_isPositiveInfinity_nid_postfix(FromBits(bits)) == 0 && uprv_isNegativeInfinity_nid_postfix(FromBits(bits)) == 0, "NaN has no infinite sign");
}
for (const double value : {0.0, -0.0, 1.0, -1.0, std::numeric_limits<double>::max(), std::numeric_limits<double>::denorm_min(), -std::numeric_limits<double>::max()}) {
Require(uprv_isNaN_nid_postfix(value) == 0, "finite values are not NaN");
Require(uprv_isInfinite_nid_postfix(value) == 0, "finite values are not infinite");
Require(uprv_isPositiveInfinity_nid_postfix(value) == 0 && uprv_isNegativeInfinity_nid_postfix(value) == 0, "finite values have no infinite sign");
}
Require(uprv_isInfinite_nid_postfix(infinity) == 1 && uprv_isInfinite_nid_postfix(-infinity) == 1, "infinities are infinite");
Require(uprv_isNaN_nid_postfix(infinity) == 0 && uprv_isNaN_nid_postfix(-infinity) == 0, "infinities are not NaN");
Require(uprv_isPositiveInfinity_nid_postfix(infinity) == 1 && uprv_isPositiveInfinity_nid_postfix(-infinity) == 0, "positive infinity");
Require(uprv_isNegativeInfinity_nid_postfix(-infinity) == 1 && uprv_isNegativeInfinity_nid_postfix(infinity) == 0, "negative infinity");
Require(Bits(uprv_getNaN_nid_postfix()) == NaNBits, "NaN bit pattern");
Require(Bits(uprv_getInfinity_nid_postfix()) == InfinityBits, "infinity bit pattern");
}
void LibraryMath() {
Require(uprv_floor_nid_postfix(-2.5) == -3.0 && uprv_floor_nid_postfix(2.5) == 2.0, "floor");
Require(uprv_ceil_nid_postfix(-2.5) == -2.0 && uprv_ceil_nid_postfix(2.5) == 3.0, "ceil");
Require(Bits(uprv_fabs_nid_postfix(-0.0)) == 0 && uprv_fabs_nid_postfix(-7.25) == 7.25, "fabs");
double integral = 0;
Require(uprv_modf_nid_postfix(-3.25, &integral) == -0.25 && integral == -3.0, "modf");
Require(Rejects([] { uprv_modf_nid_postfix(1.5, nullptr); }), "modf rejects a null integral pointer");
Require(uprv_fmod_nid_postfix(7.5, 2.0) == 1.5 && uprv_fmod_nid_postfix(-7.5, 2.0) == -1.5, "fmod");
Require(uprv_pow_nid_postfix(2.0, 10.0) == 1024.0 && uprv_pow_nid_postfix(-2.0, 3.0) == -8.0, "pow");
Require(uprv_pow10_nid_postfix(3) == 1000.0 && uprv_pow10_nid_postfix(0) == 1.0, "pow10");
Require(uprv_pow10_nid_postfix(-2) == std::pow(10.0, -2.0) && uprv_pow10_nid_postfix(400) == std::numeric_limits<double>::infinity(), "pow10 of negative and large exponents");
Require(uprv_log_nid_postfix(1.0) == 0.0 && uprv_log_nid_postfix(0.0) == -std::numeric_limits<double>::infinity(), "log");
Require(uprv_isNaN_nid_postfix(uprv_log_nid_postfix(-1.0)) == 1, "log of a negative number");
Require(uprv_maxMantissa_nid_postfix() == 18014398509481983.0, "maximum mantissa is 2^54 - 1");
}
void Rounding() {
Require(uprv_round_nid_postfix(2.5) == 3.0 && uprv_round_nid_postfix(-2.5) == -2.0, "round half up");
Require(uprv_round_nid_postfix(-2.6) == -3.0 && uprv_round_nid_postfix(2.4) == 2.0, "round to nearest");
Require(uprv_round_nid_postfix(0.49999999999999994) == 1.0, "round adds one half before flooring");
Require(Bits(uprv_round_nid_postfix(-0.5)) == 0, "round of minus one half is positive zero");
Require(uprv_trunc_nid_postfix(3.7) == 3.0 && uprv_trunc_nid_postfix(-3.7) == -3.0, "trunc");
Require(Bits(uprv_trunc_nid_postfix(-0.5)) == NegativeZeroBits && Bits(uprv_trunc_nid_postfix(-0.0)) == NegativeZeroBits, "trunc keeps the sign of zero");
Require(Bits(uprv_trunc_nid_postfix(0.5)) == 0, "trunc of a positive fraction");
Require(Bits(uprv_trunc_nid_postfix(std::numeric_limits<double>::infinity())) == InfinityBits, "trunc of positive infinity");
Require(Bits(uprv_trunc_nid_postfix(-std::numeric_limits<double>::infinity())) == InfinityBits, "trunc of negative infinity returns positive infinity");
Require(Bits(uprv_trunc_nid_postfix(FromBits(0xfff8000000000001ull))) == NaNBits, "trunc returns the canonical NaN");
}
void Extremes() {
const double nan = FromBits(0xfff8000000000001ull);
Require(Bits(uprv_fmax_nid_postfix(nan, 1.0)) == NaNBits && Bits(uprv_fmax_nid_postfix(1.0, nan)) == NaNBits, "fmax with NaN");
Require(Bits(uprv_fmin_nid_postfix(nan, 1.0)) == NaNBits && Bits(uprv_fmin_nid_postfix(1.0, nan)) == NaNBits, "fmin with NaN");
Require(uprv_fmax_nid_postfix(1.0, 2.0) == 2.0 && uprv_fmax_nid_postfix(2.0, 1.0) == 2.0, "fmax");
Require(uprv_fmin_nid_postfix(1.0, 2.0) == 1.0 && uprv_fmin_nid_postfix(2.0, 1.0) == 1.0, "fmin");
Require(uprv_fmax_nid_postfix(-std::numeric_limits<double>::infinity(), 3.0) == 3.0, "fmax with negative infinity");
Require(Bits(uprv_fmax_nid_postfix(-0.0, 0.0)) == 0 && Bits(uprv_fmax_nid_postfix(0.0, 0.0)) == 0, "fmax prefers positive zero after negative zero");
Require(Bits(uprv_fmax_nid_postfix(0.0, -0.0)) == NegativeZeroBits && Bits(uprv_fmax_nid_postfix(-0.0, -0.0)) == NegativeZeroBits, "fmax returns its second zero");
Require(Bits(uprv_fmin_nid_postfix(0.0, -0.0)) == NegativeZeroBits && Bits(uprv_fmin_nid_postfix(-0.0, 0.0)) == NegativeZeroBits, "fmin prefers negative zero");
Require(Bits(uprv_fmin_nid_postfix(0.0, 0.0)) == 0, "fmin of positive zeros");
constexpr std::int32_t low = std::numeric_limits<std::int32_t>::min();
constexpr std::int32_t high = std::numeric_limits<std::int32_t>::max();
Require(uprv_max_nid_postfix(low, high) == high && uprv_max_nid_postfix(high, low) == high && uprv_max_nid_postfix(-4, -4) == -4, "max");
Require(uprv_min_nid_postfix(low, high) == low && uprv_min_nid_postfix(high, low) == low && uprv_min_nid_postfix(7, 7) == 7, "min");
}
void Overflow() {
constexpr std::int32_t low = std::numeric_limits<std::int32_t>::min();
constexpr std::int32_t high = std::numeric_limits<std::int32_t>::max();
std::int32_t result = 0;
Require(uprv_add32_overflow_nid_postfix(2, 3, &result) == 0 && result == 5, "addition");
Require(uprv_add32_overflow_nid_postfix(high, 1, &result) == 1 && result == low, "addition overflow wraps");
Require(uprv_add32_overflow_nid_postfix(low, -1, &result) == 1 && result == high, "addition underflow wraps");
Require(uprv_add32_overflow_nid_postfix(high, low, &result) == 0 && result == -1, "addition of the extremes");
Require(uprv_mul32_overflow_nid_postfix(-46340, 46340, &result) == 0 && result == -2147395600, "multiplication");
Require(uprv_mul32_overflow_nid_postfix(65536, 65536, &result) == 1 && result == 0, "multiplication overflow wraps");
Require(uprv_mul32_overflow_nid_postfix(-46341, 46341, &result) == 1 && result == 2147479015, "negative multiplication overflow wraps");
Require(uprv_mul32_overflow_nid_postfix(low, -1, &result) == 1 && result == low, "negating the minimum overflows");
Require(Rejects([] { uprv_add32_overflow_nid_postfix(1, 2, nullptr); }), "addition rejects a null result");
Require(Rejects([] { uprv_mul32_overflow_nid_postfix(1, 2, nullptr); }), "multiplication rejects a null result");
}
void MaximumPointer() {
constexpr std::uintptr_t top = std::numeric_limits<std::uintptr_t>::max();
const auto limit = [](std::uintptr_t base) { return reinterpret_cast<std::uintptr_t>(uprv_maximumPtr_nid_postfix(reinterpret_cast<void*>(base))); };
Require(limit(0) == 0x7fffffff && limit(0x1000) == 0x80000fff, "maximum pointer adds 2^31 - 1");
Require(limit(top - 0x7fffffff) == top, "maximum pointer reaching the top");
Require(limit(top - 0x7ffffffe) == top && limit(top) == top, "maximum pointer saturates instead of wrapping");
}
}
int main() {
Classification();
LibraryMath();
Rounding();
Extremes();
Overflow();
MaximumPointer();
return 0;
}