Edit report at https://bugs.php.net/bug.php?id=79595&edit=1
ID: 79595
Updated by: cmb@php.net
Reported by: v-yitam at microsoft dot com
Summary: zend_init_fpu() alters FPU precision
Status: Open
Type: Bug
Package: Math related
Operating System: Alpine Linux
PHP Version: 7.4.5
Block user comment: N
Private report: N
New Comment:
From a comment in zend_float.h:
| This header file defines several platform-dependent macros that
| ensure equal and deterministic floating point behaviour across
| several platforms, compilers and architectures.
So, apparently, this is done deliberately. I'm not sure that this
is a good idea, though.
Previous Comments:
------------------------------------------------------------------------
[2020-05-14 17:40:43] v-yitam at microsoft dot com
I wanted to modify my original bug report but it didn't seem possible. Anyway, below is a more
accurate example to illustrate the issue. It was copied when running the debugger and stepping
through zend_init_fpu() in Zend/zend_float.c:
24 {
(gdb) s
28 if (!EG(saved_fpu_cw_ptr)) {
(gdb) n
29 EG(saved_fpu_cw_ptr) = (void*)&EG(saved_fpu_cw);
(gdb) n
31 XPFPA_STORE_CW(EG(saved_fpu_cw_ptr));
(gdb) n
32 long double n = 1;
(gdb) n
33 printf("Before %.17Le\n", n/100000);
(gdb) n
Before 1.00000000000000000e-05
35 XPFPA_SWITCH_DOUBLE();
(gdb) n
36 printf("After %.17Le\n", n/100000);
(gdb) n
After 1.00000000000000008e-05
40 }
(gdb)
------------------------------------------------------------------------
[2020-05-13 23:48:13] v-yitam at microsoft dot com
Description:
------------
A PHP extension which depends on the FPU precision being the default of the ABI (80 bit) will
produce subtly incorrect results with floating point number.
In particular, it breaks any musl libc-linked binaries that use floating point strtod(). In Alpine
Linux, musl's strtod (https://git.musl-libc.org/cgit/musl/tree/src/internal/floatscan.c)
depends on long double, which requires FPU set to 80-bit precision to actually work
To illustrate, I wrote two simple test scripts (one cpp and one php), using the example provided in
a similar issue (https://github.com/microsoft/WSL/issues/830):
The output from the test program: 1.00000000000000000e-05
The output from running php: 1.00000000000000008e-5
Please see below.
Test script:
---------------
alpine:~$ more test_print.cpp
#include <stdio.h>
int main()
{
long double n = 1;
printf("%.17Le\n", n / 100000);
}
alpine:~$ g++ -std=c++11 -static -o test_print test_print.cpp
alpine:~$ ./test_print
1.00000000000000000e-05
alpine:~$ more test_print.cpp
#include <stdio.h>
int main()
{
long double n = 1;
printf("%.17Le\n", n / 100000);
}
alpine:~$ g++ -std=c++11 -static -o test_print test_print.cpp
alpine:~$ ./test_print
1.00000000000000000e-05
alpine:~$ more test_print.php
<?php
$n = 1;
printf("%.17e\n", 1/100000);
?>
alpine:~$ php test_print.php
1.00000000000000008e-5
Expected result:
----------------
FPU precision should not be changed
Actual result:
--------------
Have to save/restore FPU state and force it back to 80-bit
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=79595&edit=1