diff --git a/core/libs/prx/libc/CMakeLists.txt b/core/libs/prx/libc/CMakeLists.txt index 30aaf71ba88f..901d5ea288ea 100644 --- a/core/libs/prx/libc/CMakeLists.txt +++ b/core/libs/prx/libc/CMakeLists.txt @@ -121,6 +121,15 @@ if(BUILD_TESTING) set_tests_properties(guest_math PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_float_math_tests ${LIBS_INCLUDE_DIR}/tests/GuestFloatMath.cpp) + target_include_directories(guest_float_math_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_float_math_tests PRIVATE libc) + configure_windows_unwind(guest_float_math_tests) + add_test(NAME guest_float_math COMMAND guest_float_math_tests) + if(WIN32) + set_tests_properties(guest_float_math PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_execute_once_tests ${LIBS_INCLUDE_DIR}/tests/GuestExecuteOnce.cpp) target_include_directories(guest_execute_once_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_execute_once_tests PRIVATE libc) diff --git a/core/libs/prx/libc/src/Math.cpp b/core/libs/prx/libc/src/Math.cpp index 1ae4fb350c71..8630b98b19e2 100644 --- a/core/libs/prx/libc/src/Math.cpp +++ b/core/libs/prx/libc/src/Math.cpp @@ -23,6 +23,9 @@ double APS5_VABI hypot_nid_postfix(double x, double y) { return std::hypot(x, y) float APS5_VABI tanf_nid_postfix(float x) { return std::tan(x); } float APS5_VABI log10f_nid_postfix(float x) { return std::log10(x); } float APS5_VABI logbf_nid_postfix(float x) { return std::logb(x); } +double APS5_VABI logb_nid_postfix(double x) { return std::logb(x); } +float APS5_VABI sinhf_nid_postfix(float x) { return std::sinh(x); } +float APS5_VABI nearbyintf_nid_postfix(float x) { return std::nearbyint(x); } double APS5_VABI exp2_nid_postfix(double x) { return std::exp2(x); } double APS5_VABI ldexp_nid_postfix(double x, int exponent) { return std::ldexp(x, exponent); } double APS5_VABI scalbn_nid_postfix(double x, int exponent) { return std::scalbn(x, exponent); } @@ -107,6 +110,18 @@ short APS5_VABI _FDtest_nid_postfix(const float* value) { int APS5_VABI __isnanf_nid_postfix(float x) { return std::isnan(x) ? 1 : 0; } int APS5_VABI __signbitf_nid_postfix(float x) { return std::signbit(x) ? 1 : 0; } +float APS5_VABI __powisf2_nid_postfix(float base, int exponent) { + const bool reciprocal = exponent < 0; + float result = 1.0f; + while (true) { + if (exponent & 1) result *= base; + exponent /= 2; + if (exponent == 0) break; + base *= base; + } + return reciprocal ? 1.0f / result : result; +} + static std::mutex g_randLock; static std::uint32_t g_randState = 1; diff --git a/core/libs/tests/GuestFloatMath.cpp b/core/libs/tests/GuestFloatMath.cpp new file mode 100644 index 000000000000..55873387a031 --- /dev/null +++ b/core/libs/tests/GuestFloatMath.cpp @@ -0,0 +1,81 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include +#include +#include + +extern "C" { +double APS5_VABI logb_nid_postfix(double); +float APS5_VABI sinhf_nid_postfix(float); +float APS5_VABI nearbyintf_nid_postfix(float); +float APS5_VABI __powisf2_nid_postfix(float, int); +} + +static void Require(bool value) { if (!value) std::abort(); } + +static void CheckLogbSinhfNearbyintf() { + const auto infinity = std::numeric_limits::infinity(); + Require(logb_nid_postfix(8.) == 3. && logb_nid_postfix(-0.75) == -1.); + Require(logb_nid_postfix(std::numeric_limits::denorm_min()) == -1074.); + Require(logb_nid_postfix(std::numeric_limits::max()) == 1023.); + Require(logb_nid_postfix(0.) == -infinity && logb_nid_postfix(-0.) == -infinity); + Require(logb_nid_postfix(-infinity) == infinity); + Require(std::isnan(logb_nid_postfix(std::numeric_limits::quiet_NaN()))); + const auto infinityf = std::numeric_limits::infinity(); + Require(sinhf_nid_postfix(0.f) == 0.f && !std::signbit(sinhf_nid_postfix(0.f))); + Require(sinhf_nid_postfix(-0.f) == 0.f && std::signbit(sinhf_nid_postfix(-0.f))); + Require(std::abs(sinhf_nid_postfix(1.f) - 1.1752012f) < 0.000001f); + Require(std::abs(sinhf_nid_postfix(-2.f) + 3.6268604f) < 0.000001f); + Require(sinhf_nid_postfix(100.f) == infinityf && sinhf_nid_postfix(-100.f) == -infinityf); + Require(sinhf_nid_postfix(infinityf) == infinityf && sinhf_nid_postfix(-infinityf) == -infinityf); + Require(std::isnan(sinhf_nid_postfix(std::numeric_limits::quiet_NaN()))); + std::feclearexcept(FE_ALL_EXCEPT); + Require(nearbyintf_nid_postfix(2.5f) == 2.f && nearbyintf_nid_postfix(3.5f) == 4.f); + Require(nearbyintf_nid_postfix(-2.5f) == -2.f && nearbyintf_nid_postfix(1.4f) == 1.f); + Require(nearbyintf_nid_postfix(-0.4f) == 0.f && std::signbit(nearbyintf_nid_postfix(-0.4f))); + Require(nearbyintf_nid_postfix(8388609.f) == 8388609.f); + Require(std::fetestexcept(FE_INEXACT) == 0); + Require(nearbyintf_nid_postfix(-infinityf) == -infinityf); + Require(std::isnan(nearbyintf_nid_postfix(std::numeric_limits::quiet_NaN()))); + const int rounding = std::fegetround(); + std::fesetround(FE_UPWARD); + Require(nearbyintf_nid_postfix(1.1f) == 2.f && nearbyintf_nid_postfix(-1.9f) == -1.f); + std::fesetround(FE_TOWARDZERO); + Require(nearbyintf_nid_postfix(1.9f) == 1.f && nearbyintf_nid_postfix(-1.9f) == -1.f); + std::fesetround(rounding); +} + +static void CheckPowisf2() { + const auto infinity = std::numeric_limits::infinity(); + const auto maximum = std::numeric_limits::max(); + const auto minimum = std::numeric_limits::min(); + Require(__powisf2_nid_postfix(2.f, 0) == 1.f && __powisf2_nid_postfix(0.f, 0) == 1.f); + Require(__powisf2_nid_postfix(std::numeric_limits::quiet_NaN(), 0) == 1.f); + Require(__powisf2_nid_postfix(infinity, 0) == 1.f); + Require(__powisf2_nid_postfix(2.f, 1) == 2.f && __powisf2_nid_postfix(2.f, 10) == 1024.f); + Require(__powisf2_nid_postfix(-2.f, 3) == -8.f && __powisf2_nid_postfix(-2.f, 4) == 16.f); + Require(__powisf2_nid_postfix(2.f, -1) == 0.5f && __powisf2_nid_postfix(2.f, -3) == 0.125f); + Require(__powisf2_nid_postfix(-2.f, -1) == -0.5f); + Require(__powisf2_nid_postfix(0.f, -1) == infinity && __powisf2_nid_postfix(-0.f, -1) == -infinity); + Require(__powisf2_nid_postfix(infinity, -1) == 0.f && __powisf2_nid_postfix(infinity, 3) == infinity); + Require(std::isnan(__powisf2_nid_postfix(std::numeric_limits::quiet_NaN(), 2))); + Require(__powisf2_nid_postfix(1.f, minimum) == 1.f && __powisf2_nid_postfix(-1.f, minimum) == 1.f); + Require(__powisf2_nid_postfix(-1.f, std::numeric_limits::max()) == -1.f); + Require(__powisf2_nid_postfix(2.f, minimum) == 0.f && __powisf2_nid_postfix(0.5f, minimum) == infinity); + Require(__powisf2_nid_postfix(2.f, 127) == 0x1p127f && __powisf2_nid_postfix(2.f, -126) == 0x1p-126f); + Require(__powisf2_nid_postfix(2.f, 128) == infinity); + for (const int exponent : {1, -1}) { + std::feclearexcept(FE_ALL_EXCEPT); + const float result = __powisf2_nid_postfix(maximum, exponent); + Require(result == (exponent == 1 ? maximum : 1.f / maximum)); + Require(std::fetestexcept(FE_OVERFLOW) == 0); + } + std::feclearexcept(FE_ALL_EXCEPT); + Require(__powisf2_nid_postfix(0x1p100f, 2) == infinity && std::fetestexcept(FE_OVERFLOW) != 0); +} + +int main() { + CheckLogbSinhfNearbyintf(); + CheckPowisf2(); +}