diff --git a/Zend/zend_float.h b/Zend/zend_float.h index 12aa02005c2a..133f58460c33 100644 --- a/Zend/zend_float.h +++ b/Zend/zend_float.h @@ -20,6 +20,8 @@ #include "zend_portability.h" +#include + BEGIN_EXTERN_C() /* @@ -413,4 +415,21 @@ END_EXTERN_C() #endif /* FPU CONTROL */ +/* zend_init_fpu() clamps the x87 FPU to double precision (53-bit mantissa) for + * the whole request. libm's sinh() computes on the x87 unit and relies on the + * extended range to round correctly to double, so while that clamp is in place + * its result is off by one or more ULP. Restore extended precision for the call + * and truncate the result back to double. Compiles down to a plain sinh() + * everywhere XPFPA_HAVE_CW is 0, which includes x86-64 and arm64. */ +static zend_always_inline double zend_sinh(double num) +{ +#if XPFPA_HAVE_CW + XPFPA_DECLARE + XPFPA_SWITCH_DOUBLE_EXTENDED(); + XPFPA_RETURN_DOUBLE(sinh(num)); +#else + return sinh(num); +#endif +} + #endif diff --git a/ext/standard/math.c b/ext/standard/math.c index e13f153f63e2..3e4f3678775d 100644 --- a/ext/standard/math.c +++ b/ext/standard/math.c @@ -535,7 +535,7 @@ PHP_FUNCTION(sinh) ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_DOUBLE(num) ZEND_PARSE_PARAMETERS_END(); - RETURN_DOUBLE(sinh(num)); + RETURN_DOUBLE(zend_sinh(num)); } /* }}} */ diff --git a/ext/standard/tests/math/sinh_fpu_precision.phpt b/ext/standard/tests/math/sinh_fpu_precision.phpt new file mode 100644 index 000000000000..a646d388474d --- /dev/null +++ b/ext/standard/tests/math/sinh_fpu_precision.phpt @@ -0,0 +1,48 @@ +--TEST-- +sinh(): results must not lose precision when the FPU is clamped to double precision +--INI-- +serialize_precision=-1 +--FILE-- + +--EXPECT-- +-- reference points already correct on every platform -- +float(0) +float(1.1752011936438014) +float(12077476.376787629) +-- inputs the FPU clamp gets wrong -- +float(10.017874927409903) +float(74.20321057778875) +float(201.71315737027922) +float(548.3161232732465) +float(1490.4788257895502) +float(4051.54190208279) +float(11013.232874703393) +float(29937.07084924806) +float(221206.6960033301) +float(601302.1420819727)