fix(libc): compute sin, cos and tan with FreeBSD msun's algorithm, as titles' libc.prx does

sin, cos and tan returned the host's functions, whose last bit differs from
the titles' copies on Windows on 3,338,607, 3,340,843 and 8,098,628 of 320
million random arguments. The titles' copies are FreeBSD msun's s_sin.c,
s_cos.c and s_tan.c over its k_sin.c, k_cos.c, k_tan.c, e_rem_pio2.c and
k_rem_pio2.c, and set EDOM when the argument's high word is 0x7ff00000 or
0xfff00000. They now compute that algorithm and set EDOM there.

The k_rem_pio2.c port that sinf, cosf and tanf use now takes msun's input
word count and precision, so the double functions share it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
JJenkxandClaude Opus 5.5 committed 2026-10-28 08:22:57 -04:00
1 parent 2227f13661
commit 4d98e8148f
3 files changed
+399 -13

No files matched your search

+258 -12
View File
@@ -613,7 +613,6 @@ long double APS5_VABI frexpl_nid_postfix(long double x, int* exponent) {
}
double APS5_VABI exp_nid_postfix(double x) { return std::exp(x); }
double APS5_VABI atan_nid_postfix(double x) { return std::atan(x); }
double APS5_VABI tan_nid_postfix(double x) { return std::tan(x); }
double APS5_VABI log2_nid_postfix(double x) { return std::log2(x); }
double APS5_VABI log_nid_postfix(double x) { return std::log(x); }
double APS5_VABI log1p_nid_postfix(double x) {
@@ -821,7 +820,7 @@ double APS5_VABI lgamma_nid_postfix(double x) {
return std::lgamma(x);
}
static const std::int32_t TrigfIpio2[] = {
static const std::int32_t RemPio2Ipio2[] = {
0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, 0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A,
0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, 0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41,
0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8, 0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF,
@@ -830,24 +829,24 @@ static const std::int32_t TrigfIpio2[] = {
0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B,
};
static const double TrigfPio2Parts[] = {
static const double RemPio2Parts[] = {
1.57079625129699707031e+00, 7.54978941586159635335e-08, 5.39030252995776476554e-15, 3.28200341580791294123e-22,
1.27065575308067607349e-29, 1.22933308981111328932e-36, 2.73370053816464559624e-44, 2.16741683877804819444e-51,
};
static int TrigfKernelRemPio2(const double* x, double* y, int e0) {
static int KernelRemPio2(const double* x, double* y, int e0, int nx, int prec) {
const double two24 = 1.67772160000000000000e+07, twon24 = 5.96046447753906250000e-08;
std::int32_t jz, jv, jp, jk, carry, n, iq[20], i, j, k, m, q0, ih;
double z, fw, f[20], fq[20], q[20];
const std::int32_t jx = 0;
jk = 3;
const std::int32_t jx = nx - 1;
jk = prec == 0 ? 3 : 4;
jp = jk;
jv = (e0 - 3) / 24;
if (jv < 0) jv = 0;
q0 = e0 - 24 * (jv + 1);
j = jv - jx;
m = jx + jk;
for (i = 0; i <= m; i++, j++) f[i] = j < 0 ? 0.0 : static_cast<double>(TrigfIpio2[j]);
for (i = 0; i <= m; i++, j++) f[i] = j < 0 ? 0.0 : static_cast<double>(RemPio2Ipio2[j]);
for (i = 0; i <= jk; i++) {
for (j = 0, fw = 0.0; j <= jx; j++) fw += x[j] * f[jx + i - j];
q[i] = fw;
@@ -906,7 +905,7 @@ recompute:
for (k = 1; iq[jk - k] == 0; k++) {
}
for (i = jz + 1; i <= jz + k; i++) {
f[jx + i] = static_cast<double>(TrigfIpio2[jv + i]);
f[jx + i] = static_cast<double>(RemPio2Ipio2[jv + i]);
for (j = 0, fw = 0.0; j <= jx; j++) fw += x[j] * f[jx + i - j];
q[i] = fw;
}
@@ -939,12 +938,17 @@ recompute:
fw *= twon24;
}
for (i = jz; i >= 0; i--) {
for (fw = 0.0, k = 0; k <= jp && k <= jz - i; k++) fw += TrigfPio2Parts[k] * q[i + k];
for (fw = 0.0, k = 0; k <= jp && k <= jz - i; k++) fw += RemPio2Parts[k] * q[i + k];
fq[jz - i] = fw;
}
fw = 0.0;
for (i = jz; i >= 0; i--) fw += fq[i];
y[0] = ih == 0 ? fw : -fw;
if (prec != 0) {
fw = fq[0] - fw;
for (i = 1; i <= jz; i++) fw += fq[i];
y[1] = ih == 0 ? fw : -fw;
}
return n & 7;
}
@@ -969,7 +973,7 @@ static int TrigfRemPio2(float x, double* y) {
const std::int32_t e0 = (ix >> 23) - 150;
const float z = std::bit_cast<float>(ix - static_cast<std::int32_t>(static_cast<std::uint32_t>(e0) << 23));
double tx[1] = {z}, ty[1];
const int n = TrigfKernelRemPio2(tx, ty, e0);
const int n = KernelRemPio2(tx, ty, e0, 1, 0);
if (hx < 0) {
*y = -ty[0];
return -n;
@@ -1099,8 +1103,231 @@ void APS5_VABI sincosf_nid_postfix(float x, float* sinp, float* cosp) {
*cosp = cosf_nid_postfix(x);
}
double APS5_VABI sin_nid_postfix(double x) { return std::sin(x); }
double APS5_VABI cos_nid_postfix(double x) { return std::cos(x); }
static std::int32_t HighWord(double x) {
return static_cast<std::int32_t>(std::bit_cast<std::uint64_t>(x) >> 32);
}
static double ClearLowWord(double x) {
return std::bit_cast<double>(std::bit_cast<std::uint64_t>(x) & 0xffffffff00000000ull);
}
static int RemPio2Near(double x, double* y, std::int32_t hx, int k) {
const double pio2_1 = 1.57079632673412561417e+00;
const double pio2_1t = 6.07710050650619224932e-11;
if (hx > 0) {
const double z = x - k * pio2_1;
y[0] = z - k * pio2_1t;
y[1] = (z - y[0]) - k * pio2_1t;
return k;
}
const double z = x + k * pio2_1;
y[0] = z + k * pio2_1t;
y[1] = (z - y[0]) + k * pio2_1t;
return -k;
}
static int RemPio2(double x, double* y) {
const double two24 = 1.67772160000000000000e+07;
const double invpio2 = 6.36619772367581382433e-01;
const double pio2_1 = 1.57079632673412561417e+00;
const double pio2_1t = 6.07710050650619224932e-11;
const double pio2_2 = 6.07710050630396597660e-11;
const double pio2_2t = 2.02226624879595063154e-21;
const double pio2_3 = 2.02226624871116645580e-21;
const double pio2_3t = 8.47842766036889956997e-32;
const std::int32_t hx = HighWord(x);
const std::int32_t ix = hx & 0x7fffffff;
bool medium = false;
if (ix <= 0x400f6a7a) {
if ((ix & 0xfffff) != 0x921fb) {
return RemPio2Near(x, y, hx, ix <= 0x4002d97c ? 1 : 2);
}
medium = true;
} else if (ix <= 0x401c463b) {
if (ix <= 0x4015fdbc) {
if (ix != 0x4012d97c) {
return RemPio2Near(x, y, hx, 3);
}
} else if (ix != 0x401921fb) {
return RemPio2Near(x, y, hx, 4);
}
medium = true;
}
if (medium || ix < 0x413921fb) {
double fn = x * invpio2 + 0x1.8p52;
fn = fn - 0x1.8p52;
const std::int32_t n = static_cast<std::int32_t>(fn);
double r = x - fn * pio2_1;
double w = fn * pio2_1t;
const std::int32_t j = ix >> 20;
y[0] = r - w;
std::int32_t i = j - ((HighWord(y[0]) >> 20) & 0x7ff);
if (i > 16) {
double t = r;
w = fn * pio2_2;
r = t - w;
w = fn * pio2_2t - ((t - r) - w);
y[0] = r - w;
i = j - ((HighWord(y[0]) >> 20) & 0x7ff);
if (i > 49) {
t = r;
w = fn * pio2_3;
r = t - w;
w = fn * pio2_3t - ((t - r) - w);
y[0] = r - w;
}
}
y[1] = (r - y[0]) - w;
return n;
}
if (ix >= 0x7ff00000) {
y[0] = y[1] = x - x;
return 0;
}
const std::uint32_t low = static_cast<std::uint32_t>(std::bit_cast<std::uint64_t>(x));
const std::int32_t e0 = (ix >> 20) - 1046;
double z = std::bit_cast<double>((static_cast<std::uint64_t>(static_cast<std::uint32_t>(ix - (e0 << 20))) << 32) | low);
double tx[3];
double ty[2];
for (int i = 0; i < 2; i++) {
tx[i] = static_cast<double>(static_cast<std::int32_t>(z));
z = (z - tx[i]) * two24;
}
tx[2] = z;
int nx = 3;
while (tx[nx - 1] == 0.0) nx--;
const int n = KernelRemPio2(tx, ty, e0, nx, 1);
if (hx < 0) {
y[0] = -ty[0];
y[1] = -ty[1];
return -n;
}
y[0] = ty[0];
y[1] = ty[1];
return n;
}
static double KernelSin(double x, double y, int iy) {
const double S1 = -1.66666666666666324348e-01;
const double S2 = 8.33333333332248946124e-03;
const double S3 = -1.98412698298579493134e-04;
const double S4 = 2.75573137070700676789e-06;
const double S5 = -2.50507602534068634195e-08;
const double S6 = 1.58969099521155010221e-10;
const double z = x * x;
const double w = z * z;
const double r = S2 + z * (S3 + z * S4) + z * w * (S5 + z * S6);
const double v = z * x;
if (iy == 0) {
return x + v * (S1 + z * r);
}
return x - ((z * (0.5 * y - v * r) - y) - v * S1);
}
static double KernelCos(double x, double y) {
const double C1 = 4.16666666666666019037e-02;
const double C2 = -1.38888888888741095749e-03;
const double C3 = 2.48015872894767294178e-05;
const double C4 = -2.75573143513906633035e-07;
const double C5 = 2.08757232129817482790e-09;
const double C6 = -1.13596475577881948265e-11;
const double z = x * x;
double w = z * z;
const double r = z * (C1 + z * (C2 + z * C3)) + w * w * (C4 + z * (C5 + z * C6));
const double hz = 0.5 * z;
w = 1.0 - hz;
return w + (((1.0 - w) - hz) + (z * r - x * y));
}
static double KernelTan(double x, double y, int iy) {
static constexpr double T[] = {
3.33333333333334091986e-01, 1.33333333333201242699e-01, 5.39682539762260521377e-02, 2.18694882948595424599e-02,
8.86323982359930005737e-03, 3.59207910759131235356e-03, 1.45620945432529025516e-03, 5.88041240820264096874e-04,
2.46463134818469906812e-04, 7.81794442939557092300e-05, 7.14072491382608190305e-05, -1.85586374855275456654e-05,
2.59073051863633712884e-05,
};
const double pio4 = 7.85398163397448278999e-01;
const double pio4lo = 3.06161699786838301793e-17;
const std::int32_t hx = HighWord(x);
const std::int32_t ix = hx & 0x7fffffff;
if (ix >= 0x3fe59428) {
if (hx < 0) {
x = -x;
y = -y;
}
const double z = pio4 - x;
const double w = pio4lo - y;
x = z + w;
y = 0.0;
}
const double z = x * x;
double w = z * z;
double r = T[1] + w * (T[3] + w * (T[5] + w * (T[7] + w * (T[9] + w * T[11]))));
double v = z * (T[2] + w * (T[4] + w * (T[6] + w * (T[8] + w * (T[10] + w * T[12])))));
double s = z * x;
r = y + z * (s * (r + v) + y);
r += T[0] * s;
w = x + r;
if (ix >= 0x3fe59428) {
v = static_cast<double>(iy);
return static_cast<double>(1 - ((hx >> 30) & 2)) * (v - 2.0 * (x - (w * w / (w + v) - r)));
}
if (iy == 1) {
return w;
}
const double head = ClearLowWord(w);
v = r - (head - x);
const double a = -1.0 / w;
const double t = ClearLowWord(a);
s = 1.0 + t * head;
return t + a * (s + t * v);
}
double APS5_VABI sin_nid_postfix(double x) {
const std::int32_t ix = HighWord(x) & 0x7fffffff;
if (ix <= 0x3fe921fb) {
if (ix < 0x3e500000 && static_cast<int>(x) == 0) {
return x;
}
return KernelSin(x, 0.0, 0);
}
if (ix >= 0x7ff00000) {
if (ix == 0x7ff00000) {
errno = EDOM;
}
return x - x;
}
double y[2];
switch (RemPio2(x, y) & 3) {
case 0: return KernelSin(y[0], y[1], 1);
case 1: return KernelCos(y[0], y[1]);
case 2: return -KernelSin(y[0], y[1], 1);
default: return -KernelCos(y[0], y[1]);
}
}
double APS5_VABI cos_nid_postfix(double x) {
const std::int32_t ix = HighWord(x) & 0x7fffffff;
if (ix <= 0x3fe921fb) {
if (ix < 0x3e46a09e && static_cast<int>(x) == 0) {
return 1.0;
}
return KernelCos(x, 0.0);
}
if (ix >= 0x7ff00000) {
if (ix == 0x7ff00000) {
errno = EDOM;
}
return x - x;
}
double y[2];
switch (RemPio2(x, y) & 3) {
case 0: return KernelCos(y[0], y[1]);
case 1: return -KernelSin(y[0], y[1], 1);
case 2: return -KernelCos(y[0], y[1]);
default: return KernelSin(y[0], y[1], 1);
}
}
/*-
* Copyright (c) 2011 David Schultz <[email protected]>
* All rights reserved.
@@ -1149,6 +1376,25 @@ static double SinhLdexpExpHalf(double x) {
* ====================================================
*/
/* Adapted from FreeBSD release/11.4.0 lib/msun/src/e_sinh.c: https://github.com/freebsd/freebsd-src/blob/release/11.4.0/lib/msun/src/e_sinh.c */
double APS5_VABI tan_nid_postfix(double x) {
const std::int32_t ix = HighWord(x) & 0x7fffffff;
if (ix <= 0x3fe921fb) {
if (ix < 0x3e400000 && static_cast<int>(x) == 0) {
return x;
}
return KernelTan(x, 0.0, 1);
}
if (ix >= 0x7ff00000) {
if (ix == 0x7ff00000) {
errno = EDOM;
}
return x - x;
}
double y[2];
const int n = RemPio2(x, y);
return KernelTan(y[0], y[1], 1 - ((n & 1) << 1));
}
double APS5_VABI sinh_nid_postfix(double x) {
const double one = 1.0;
const double shuge = 1.0e307;
+139
View File
@@ -36,6 +36,100 @@ extern SonyDconst _LSnan_nid_postfix;
extern int signgam_nid_postfix;
float APS5_VABI logbf_nid_postfix(float);
int APS5_VABI ilogb_nid_postfix(double);
int APS5_VABI ilogbf_nid_postfix(float);
double APS5_VABI exp2_nid_postfix(double);
float APS5_VABI __powisf2_nid_postfix(float, int);
double APS5_VABI __powidf2_nid_postfix(double, int);
double APS5_VABI ldexp_nid_postfix(double, int);
double APS5_VABI scalbn_nid_postfix(double, int);
float APS5_VABI scalbnf_nid_postfix(float, int);
float APS5_VABI nextafterf_nid_postfix(float, float);
double APS5_VABI nextafter_nid_postfix(double, double);
double APS5_VABI frexp_nid_postfix(double, int*);
float APS5_VABI frexpf_nid_postfix(float, int*);
std::int64_t APS5_VABI lround_nid_postfix(double);
std::div_t APS5_VABI div_nid_postfix(int, int);
double APS5_VABI sinh_nid_postfix(double);
double APS5_VABI cosh_nid_postfix(double);
double APS5_VABI _Cosh_nid_postfix(double, double);
float APS5_VABI coshf_nid_postfix(float);
float APS5_VABI sinhf_nid_postfix(float);
float APS5_VABI _FSinh_nid_postfix(float, float);
float APS5_VABI _FCosh_nid_postfix(float, float);
double APS5_VABI asinh_nid_postfix(double);
float APS5_VABI asinhf_nid_postfix(float);
double APS5_VABI acosh_nid_postfix(double);
float APS5_VABI acoshf_nid_postfix(float);
double APS5_VABI atanh_nid_postfix(double);
double APS5_VABI sin_nid_postfix(double);
double APS5_VABI cos_nid_postfix(double);
double APS5_VABI tan_nid_postfix(double);
float APS5_VABI atanhf_nid_postfix(float);
std::int64_t APS5_VABI lroundf_nid_postfix(float);
std::int64_t APS5_VABI llround_nid_postfix(double);
std::int64_t APS5_VABI llroundf_nid_postfix(float);
std::int64_t APS5_VABI llroundl_nid_postfix(long double);
int APS5_VABI __isfinitef_nid_postfix(float);
int APS5_VABI __isfinitel_nid_postfix(long double);
int APS5_VABI __isnormal_nid_postfix(double);
int APS5_VABI __isnormalf_nid_postfix(float);
int APS5_VABI __isinff_nid_postfix(float);
int APS5_VABI __isinf_nid_postfix(double);
int APS5_VABI isnan_nid_postfix(double);
int APS5_VABI isinf_nid_postfix(double);
std::lldiv_t APS5_VABI lldiv_nid_postfix(long long, long long);
std::lldiv_t APS5_VABI imaxdiv_nid_postfix(std::int64_t, std::int64_t);
std::int64_t APS5_VABI labs_nid_postfix(std::int64_t);
std::int64_t APS5_VABI llabs_nid_postfix(std::int64_t);
std::int64_t APS5_VABI imaxabs_nid_postfix(std::int64_t);
long double APS5_VABI scalbnl_nid_postfix(long double, int);
float APS5_VABI significandf_nid_postfix(float);
double APS5_VABI significand_nid_postfix(double);
long double APS5_VABI acoshl_nid_postfix(long double);
long double APS5_VABI asinhl_nid_postfix(long double);
long double APS5_VABI atanhl_nid_postfix(long double);
long double APS5_VABI atan2l_nid_postfix(long double, long double);
long double APS5_VABI cbrtl_nid_postfix(long double);
long double APS5_VABI erfl_nid_postfix(long double);
long double APS5_VABI erfcl_nid_postfix(long double);
long double APS5_VABI expm1l_nid_postfix(long double);
long double APS5_VABI log1pl_nid_postfix(long double);
long double APS5_VABI log2l_nid_postfix(long double);
long double APS5_VABI lgammal_nid_postfix(long double);
long double APS5_VABI tgammal_nid_postfix(long double);
float APS5_VABI tgammaf_nid_postfix(float);
long double APS5_VABI hypotl_nid_postfix(long double, long double);
float APS5_VABI hypot3f_nid_postfix(float, float, float);
double APS5_VABI hypot3_nid_postfix(double, double, double);
long double APS5_VABI hypot3l_nid_postfix(long double, long double, long double);
float APS5_VABI j0f_nid_postfix(float);
float APS5_VABI j1f_nid_postfix(float);
float APS5_VABI jnf_nid_postfix(int, float);
float APS5_VABI y0f_nid_postfix(float);
float APS5_VABI y1f_nid_postfix(float);
float APS5_VABI ynf_nid_postfix(int, float);
std::lldiv_t APS5_VABI ldiv_nid_postfix(std::int64_t, std::int64_t);
float APS5_VABI nanf_nid_postfix(const char*);
double APS5_VABI nan_nid_postfix(const char*);
long double APS5_VABI atanl_nid_postfix(long double);
long double APS5_VABI sinl_nid_postfix(long double);
long double APS5_VABI cosl_nid_postfix(long double);
long double APS5_VABI tanl_nid_postfix(long double);
long double APS5_VABI sinhl_nid_postfix(long double);
long double APS5_VABI coshl_nid_postfix(long double);
long double APS5_VABI tanhl_nid_postfix(long double);
long double APS5_VABI expl_nid_postfix(long double);
long double APS5_VABI exp2l_nid_postfix(long double);
long double APS5_VABI logl_nid_postfix(long double);
long double APS5_VABI log10l_nid_postfix(long double);
long double APS5_VABI powl_nid_postfix(long double, long double);
long double APS5_VABI ldexpl_nid_postfix(long double, int);
long double APS5_VABI truncl_nid_postfix(long double);
long double APS5_VABI nearbyintl_nid_postfix(long double);
double APS5_VABI logb_nid_postfix(double);
double APS5_VABI remainder_nid_postfix(double, double);
struct DinkumwareFloatCharacteristics {
int digits;
int mantissaDigits;
@@ -1038,6 +1132,50 @@ static void CheckExpm1ScalingRounding() {
}
_mm_setcsr(savedCsr);
}
static void CheckTrigConsoleBits() {
constexpr struct {
std::uint64_t input;
std::uint64_t sine;
std::uint64_t cosine;
std::uint64_t tangent;
int error;
} cases[] = {
{0x0000000000000000ull, 0x0000000000000000ull, 0x3ff0000000000000ull, 0x0000000000000000ull, 0},
{0x8000000000000000ull, 0x8000000000000000ull, 0x3ff0000000000000ull, 0x8000000000000000ull, 0},
{0x3e3fffffffffffffull, 0x3e3fffffffffffffull, 0x3ff0000000000000ull, 0x3e3fffffffffffffull, 0},
{0x3fe921fb54442d18ull, 0x3fe6a09e667f3bccull, 0x3fe6a09e667f3bcdull, 0x3fefffffffffffffull, 0},
{0x3ff921fb54442d18ull, 0x3ff0000000000000ull, 0x3c91a62633145c07ull, 0x434d02967c31cdb5ull, 0},
{0x400921fb54442d18ull, 0x3ca1a62633145c07ull, 0xbff0000000000000ull, 0xbca1a62633145c07ull, 0},
{0xc00921fb54442d18ull, 0xbca1a62633145c07ull, 0xbff0000000000000ull, 0x3ca1a62633145c07ull, 0},
{0x401921fb54442d18ull, 0xbcb1a62633145c07ull, 0x3ff0000000000000ull, 0xbcb1a62633145c07ull, 0},
{0x4012d97c7f3321d2ull, 0xbff0000000000000ull, 0xbcaa79394c9e8a0aull, 0x4333570efd768923ull, 0},
{0x412e848000000000ull, 0xbfd6664b2568d867ull, 0x3fedf9df9906d32cull, 0xbfd7e9768ab734c0ull, 0},
{0x4139000000000000ull, 0x3fd065dff07cc910ull, 0xbfeeee8b0ce93021ull, 0xbfd0f6d7d15bc5b1ull, 0},
{0x7e37e43c8800759cull, 0xbfea2c16b010e385ull, 0xbfe2699022adc4c1ull, 0x3ff6be411f37ac77ull, 0},
{0x7fefffffffffffffull, 0x3f7452fc98b34e97ull, 0xbfefffe62ecfab75ull, 0xbf74530cfe729484ull, 0},
{0xffefffffffffffffull, 0xbf7452fc98b34e97ull, 0xbfefffe62ecfab75ull, 0x3f74530cfe729484ull, 0},
{0x7ff0000000000000ull, 0xfff8000000000000ull, 0xfff8000000000000ull, 0xfff8000000000000ull, EDOM},
{0xfff0000000000000ull, 0xfff8000000000000ull, 0xfff8000000000000ull, 0xfff8000000000000ull, EDOM},
{0x7ff0000054442d18ull, 0x7ff8000054442d18ull, 0x7ff8000054442d18ull, 0x7ff8000054442d18ull, EDOM},
{0x7ff8000000000001ull, 0x7ff8000000000001ull, 0x7ff8000000000001ull, 0x7ff8000000000001ull, 0},
{0x3fdada3603b109ebull, 0x3fda124ac129e754ull, 0x3fed397712faa550ull, 0x3fdc8c2adf939d0aull, 0},
{0xbfba382e79c9f161ull, 0xbfba2c73e99f121cull, 0x3fefd51240491c33ull, 0xbfba4fc011ff9a31ull, 0},
{0x4079ab389c31a606ull, 0x3fe7fbf5be09b71cull, 0xbfe52f138550db6full, 0xbff21d74a8b63e76ull, 0},
{0xbfd6066a236944ecull, 0xbfd597c69592a402ull, 0x3fee1faa6b709b8bull, 0xbfd6f0152b51d55cull, 0},
{0xbfcdf9356ad39d8full, 0xbfcdb34648619610ull, 0x3fef206c15f26ee6ull, 0xbfce889b662f705full, 0},
{0x40557a01c7c97d05ull, 0xbfec45eb63779e2eull, 0xbfddf9b1098520ebull, 0x3ffe2ecb59bfb87eull, 0},
};
for (const auto& item : cases) {
const double x = std::bit_cast<double>(item.input);
const int expected = item.error != 0 ? item.error : 1234;
errno = 1234;
Require(std::bit_cast<std::uint64_t>(sin_nid_postfix(x)) == item.sine && errno == expected);
errno = 1234;
Require(std::bit_cast<std::uint64_t>(cos_nid_postfix(x)) == item.cosine && errno == expected);
errno = 1234;
Require(std::bit_cast<std::uint64_t>(tan_nid_postfix(x)) == item.tangent && errno == expected);
}
}
static void CheckAsinl() {
const struct { std::uint64_t inputMantissa; std::uint16_t inputSignExponent; std::uint64_t outputMantissa; std::uint16_t outputSignExponent; } cases[] = {
@@ -2369,6 +2507,7 @@ int main() {
CheckNextafterEnvironment(nextafterf_nid_postfix, 0x80000000u, 0x3f800000u, 0x00800000u, 0x7f7fffffu, 0x7f800000u);
CheckNextafterEnvironment(nextafter_nid_postfix, std::uint64_t{0x8000000000000000ull}, std::uint64_t{0x3ff0000000000000ull}, std::uint64_t{0x0010000000000000ull}, std::uint64_t{0x7fefffffffffffffull}, std::uint64_t{0x7ff0000000000000ull});
CheckExpm1ScalingRounding();
CheckTrigConsoleBits();
CheckAsinl();
Require(scalbn_nid_postfix(0.75, -2) == 0.1875);
Require(scalbnf_nid_postfix(0.75f, 4) == 12.f);
+2 -1
View File
@@ -117,7 +117,8 @@
- [Guest path names](../../../core/libs/prx/libc/src/General.cpp) (libc) are checked against FreeBSD 11.4's `NAME_MAX` (255 bytes per component) and `MAXPATHLEN` (1024 bytes with the terminating NUL) before they are resolved, and a longer name fails with `ENAMETOOLONG`, as `namei` does. The host's own limit still applies below that: on Windows a path whose host form exceeds `MAX_PATH` fails with `ENOENT`. A backslash counts as a path separator for the component length, because the guest path resolver turns it into `/` before the host call, while FreeBSD treats it as an ordinary name character, so a 301-byte name that contains one is accepted here. `sceNgs2ParseWaveformFile` resolves its path without a try block, so a long component ends in an uncaught `filesystem_error` there where a missing file ended in a `runtime_error` (both end the title). A path through a regular file (`file/x`, `file/`) is not always `ENOTDIR` on the calls that resolve it. None of this was measured on a console.
- [Guest path names](../../../core/libs/prx/libc/src/General.cpp) (libc) fail on macOS with `ENAMETOOLONG` once their host form, the root directory followed by the guest path, reaches 1024 bytes, so a guest path of 1023 bytes, which FreeBSD accepts, fails there under any root directory
- [ftrylockfile](../../../core/libs/prx/libc/src/RuntimeSupport.cpp) (libc) - tries the host CRT stream lock that `flockfile`, `funlockfile` and each locking host stdio call on the stream take, so it returns -1 while another thread holds the stream through `flockfile` or is inside such a call, and 0 when it acquires the lock or already owns it (recursively, released by as many `funlockfile` calls), as FreeBSD 11 [`_ftrylockfile`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdio/_flock_stub.c#L89-L104) does. A libc export holds the lock only for each host call it makes, not across several host calls or while it updates the guest `FILE` prefix. Linux uses glibc `ftrylockfile`. UCRT has no try variant of `_lock_file`, so Windows calls `TryEnterCriticalSection` on the `CRITICAL_SECTION` that `_lock_file` enters, found through the private UCRT stream layout (`__crt_stdio_stream_data` in `ucrt/inc/corecrt_internal_stdio.h`, unchanged from Windows SDK 10.0.19041 to 10.0.26100); another CRT or a change of that layout breaks it. A null or closed stream throws where FreeBSD dereferences it
- [sinf, cosf, tanf and sincosf](../../../core/libs/prx/libc/src/Math.cpp) (libc) are FreeBSD msun's [`s_sinf.c`, `s_cosf.c` and `s_tanf.c`](https://github.com/freebsd/freebsd-src/tree/release/11.4.0/lib/msun/src) over its double-precision kernels and `e_rem_pio2f.c`, with `k_rem_pio2.c` (for `|x| >= 2^28 * pi/2`) ported only for one input word and `prec` 0; `sincosf` returns the two. As in the `libc.prx` that titles ship, only an infinite argument sets `errno` (`EDOM`); NaN leaves it unchanged. Floating-point exceptions are not compared. All four give the same bits and `errno` as the copies bundled with PPSA07923, PPSA05916 and PPSA13934 for all 2^32 arguments, run on a Windows host.
- [sin, cos and tan](../../../core/libs/prx/libc/src/Math.cpp) (libc) do not compare floating-point exceptions, and their double inputs are checked by sampling, not exhaustively. They are FreeBSD msun's [`s_sin.c`, `s_cos.c` and `s_tan.c`](https://github.com/freebsd/freebsd-src/tree/release/11.4.0/lib/msun/src) over its `k_sin.c`, `k_cos.c`, `k_tan.c`, `e_rem_pio2.c` and `k_rem_pio2.c`. As the titles' `libc.prx` does, they set `EDOM` when the argument's high word is `0x7ff00000` or `0xfff00000`, the infinities and the NaNs that share that high word, and leave `errno` alone for other NaNs. They give the same bits as the copies bundled with PPSA07923 and PPSA05916 on 320 million random and 69,316 structured arguments on a Windows host; the FMA-contracted build bundled with PPSA13934 differs on about 0.35% (`sin`, `cos`) and 0.7% (`tan`) of the random ones.
- [sinf, cosf, tanf and sincosf](../../../core/libs/prx/libc/src/Math.cpp) (libc) are FreeBSD msun's [`s_sinf.c`, `s_cosf.c` and `s_tanf.c`](https://github.com/freebsd/freebsd-src/tree/release/11.4.0/lib/msun/src) over its double-precision kernels and `e_rem_pio2f.c`, with `k_rem_pio2.c` (for `|x| >= 2^28 * pi/2`) shared with `sin`, `cos` and `tan`; `sincosf` returns the two. As in the `libc.prx` that titles ship, only an infinite argument sets `errno` (`EDOM`); NaN leaves it unchanged. Floating-point exceptions are not compared. All four give the same bits and `errno` as the copies bundled with PPSA07923, PPSA05916 and PPSA13934 for all 2^32 arguments, run on a Windows host.
- [fgetln](../../../core/libs/prx/libc/src/StandardStreams.cpp) (libSceLibcInternal, implemented in libc) throws: in the console's `libSceLibcInternal.sprx` (SDK 4.5x build of `J03950001`, 0x5FE80) the export is a `hlt` instruction, which faults in user mode and ends the process
- [Wide-character conversions](../../../core/libs/prx/libc/src/Multibyte.cpp) (libc) - The wide `wcsto*` functions parse the ASCII prefix of the guest string, as the C locale accepts only ASCII white space and digits. The multibyte conversions follow the C locale of the console's `libSceLibcInternal.sprx`: UTF-8 of at most three bytes, so `_Getmbcurmax` returns 3, overlong forms and encoded surrogates decode, and four-byte forms are invalid. `__mb_cur_max` stays 1 like the console's variable; `___mb_cur_max` is not in that module and also stays 1, unverified. Other locales are not supported. With a null destination, `wcstombs` returns the byte count; the console's count is wrong when the capacity argument equals the running count, because it reads past the terminator
- [realpath](../../../core/libs/prx/libc/src/RealPath.cpp) (libc) follows FreeBSD 11's [`realpath.c`](https://github.com/freebsd/freebsd-src/blob/stable/11/lib/libc/stdlib/realpath.c) with two gaps. A host symbolic link with a relative target under the guest root is expanded as `realpath1` does (a dangling link fails with `ENOENT`, more than 32 links with `ELOOP`), but a link with an absolute target is not expanded, nor is anything below it, because the target is a host path with no guest path to map to. Only Windows symbolic links are expanded there, not junctions. Expansion works on the guest path, so a host link whose text climbs above a path alias root is resolved against the guest tree, not the host one. Otherwise `..` drops the previous component lexically, as `Resolve` does for every guest path.