Files
AnyPS5/core/libs/tests/GuestFminf.cpp
T
bluandClaude Opus 5.5 213327fede fix(libc): keep fmin and fmax quiet on a signalling NaN
fmin, fmax and their f and l forms tested their operands with std::isnan,
which GCC compiles to a compare that raises FE_INVALID for a signalling
NaN. FreeBSD's s_fmax.c and s_fmin.c read the exponent and significand
bits instead, "to avoid raising spurious exceptions", and return the
other operand. The NaN test now reads the bits of float, double and the
80-bit long double.

GuestFminf checks that a signalling NaN on either side returns the other
operand and leaves FE_INVALID clear, for all three types.

The failure was first caught by the numeric helper test of
boykopovar/AnyPS5#2280 (Привалов Александр Владимирович).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 21:00:40 -07:00

80 lines
3.2 KiB
C++

#include "prx/libc/include/general/VabiMacros.hpp"
#include <cfenv>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <limits>
extern "C" {
double APS5_VABI fmin_nid_postfix(double, double);
float APS5_VABI fminf_nid_postfix(float, float);
long double APS5_VABI fminl_nid_postfix(long double, long double);
double APS5_VABI fmax_nid_postfix(double, double);
float APS5_VABI fmaxf_nid_postfix(float, float);
long double APS5_VABI fmaxl_nid_postfix(long double, long double);
}
static void Check(bool condition, const char* name, int line) {
if (!condition) {
std::fprintf(stderr, "%s check failed at line %d\n", name, line);
std::abort();
}
}
#define Require(value) Check((value), name, __LINE__)
template <typename T>
static void CheckMinAndMax(T (APS5_VABI *minimum)(T, T), T (APS5_VABI *maximum)(T, T), const char* name) {
const auto nan = std::numeric_limits<T>::quiet_NaN();
const auto infinity = std::numeric_limits<T>::infinity();
Require(minimum(T(3.0), T(-2.0)) == T(-2.0));
Require(minimum(T(-2.0), T(3.0)) == T(-2.0));
Require(minimum(T(1.5), T(2.5)) == T(1.5));
Require(maximum(T(3.0), T(-2.0)) == T(3.0));
Require(maximum(T(-2.0), T(3.0)) == T(3.0));
Require(maximum(T(1.5), T(2.5)) == T(2.5));
Require(minimum(nan, T(4.0)) == T(4.0));
Require(minimum(T(4.0), nan) == T(4.0));
Require(std::isnan(minimum(nan, nan)));
Require(maximum(nan, T(4.0)) == T(4.0));
Require(maximum(T(4.0), nan) == T(4.0));
Require(std::isnan(maximum(nan, nan)));
const auto signaling = std::numeric_limits<T>::signaling_NaN();
std::feclearexcept(FE_ALL_EXCEPT);
Require(minimum(signaling, T(4.0)) == T(4.0) && minimum(T(4.0), signaling) == T(4.0));
Require(maximum(signaling, T(4.0)) == T(4.0) && maximum(T(4.0), signaling) == T(4.0));
Require(std::fetestexcept(FE_INVALID) == 0);
Require(std::signbit(minimum(T(-0.0), T(+0.0))));
Require(std::signbit(minimum(T(+0.0), T(-0.0))));
Require(std::signbit(minimum(T(-0.0), T(-0.0))));
Require(!std::signbit(minimum(T(+0.0), T(+0.0))));
Require(!std::signbit(maximum(T(-0.0), T(+0.0))));
Require(!std::signbit(maximum(T(+0.0), T(-0.0))));
Require(std::signbit(maximum(T(-0.0), T(-0.0))));
Require(!std::signbit(maximum(T(+0.0), T(+0.0))));
Require(minimum(infinity, -infinity) == -infinity);
Require(minimum(-infinity, infinity) == -infinity);
Require(minimum(infinity, T(5.0)) == T(5.0));
Require(minimum(T(5.0), infinity) == T(5.0));
Require(minimum(-infinity, T(5.0)) == -infinity);
Require(minimum(T(5.0), -infinity) == -infinity);
Require(maximum(infinity, -infinity) == infinity);
Require(maximum(-infinity, infinity) == infinity);
Require(maximum(infinity, T(5.0)) == infinity);
Require(maximum(T(5.0), infinity) == infinity);
Require(maximum(-infinity, T(5.0)) == T(5.0));
Require(maximum(T(5.0), -infinity) == T(5.0));
}
int main() {
CheckMinAndMax<double>(fmin_nid_postfix, fmax_nid_postfix, "fmin/fmax");
CheckMinAndMax<float>(fminf_nid_postfix, fmaxf_nid_postfix, "fminf/fmaxf");
CheckMinAndMax<long double>(fminl_nid_postfix, fmaxl_nid_postfix, "fminl/fmaxl");
return 0;
}