Bug #70173 [Opn]: ZVAL_COPY_VALUE_EX broken for 32bit Solaris Sparc
| From: | rainer dot jung at kippdata dot de | Date: | Sat, 08 Aug 2015 20:57:19 +0000 |
| Subject: | Bug #70173 [Opn]: ZVAL_COPY_VALUE_EX broken for 32bit Solaris Sparc | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-195034@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=70173&edit=1
ID: 70173
User updated by: rainer dot jung at kippdata dot de
Reported by: rainer dot jung at kippdata dot de
-Summary: Precision problem in float
+Summary: ZVAL_COPY_VALUE_EX broken for 32bit Solaris Sparc
Status: Open
Type: Bug
Package: *General Issues
Operating System: Solaris 10 Sparc
-PHP Version: 7.0.0beta2
+PHP Version: 7.0.0beta3
Block user comment: N
Private report: N
New Comment:
Changed Summary, because I narrowed down the root cause.
Unfortunately there is no "Zend" in the issue tracker "Package" drop down.
Previous Comments:
------------------------------------------------------------------------
[2015-08-08 20:53:59] rainer dot jung at kippdata dot de
OK, I used the debugger. The problem is inside ZVAL_COPY_VALUE_EX. Only half of the double is being
copied, only 32 bits of the 64 bits. ZVAL_COPY_VALUE_EX is meant to fix this by using the "#if
SIZEOF_SIZE_T == 4" definition of it it additionally copies value.ww.w2. But on Solaris Sparc,
value.ww.w2 has already been copied and what is missing is value.ww.w1 ! For some reason the struct
layout is not the expected one, w1 and w2 have the wrong order. It seems the ZEND_ENDIAN_LOHI()
logic for w1, w2 resp. the refcount field which is used in the basic copying doesn't work on
sparc. It is a big endian platform and WORDS_BIGENDIAN is defined, but is nevertheless doesn't
work.
I hope that's enough debugging for you guys to be able to go the last mile. I can test on the
target platform whatever change you want me to.
------------------------------------------------------------------------
[2015-08-08 14:32:49] rainer dot jung at kippdata dot de
OK, another step forward, but I gues that's how far I get without any hints:
Simplest test script:
<?php
$var = 2900000000;
var_dump($var);
?>
Expected result:
----------------
float(2900000000)
Actual result:
--------------
float(2899998720)
It works for PHP 5.6, fails for 7.
- The result of zend_strtod() when printed with fprintf and %f format is
"2900000000.000000". So this is OK.
- The double assignment in Zend/zend_language_scanner.c in line 2757 assigns the correct value
(verified with fprintf and %f). If I call php_var_dump(zendlval, 0) there it writes the correct
string "float(2900000000)".
- In the var_dump() call of next script line, the float argument is already wrong. If checked via
fprointf and %f it is "2899998720.000000". The address of struc in php_var_dump() has also
changed from the previous address of zendlval in zend_language_scanner.c.
I don't know where the change happens, I could just narrow it down so far.
Just to make sure: I don't think this is an expected float precision problem. The test works
for 5.6 and the observed precision is 7 instead of 14. It makes a lot of standard test suite tests
fail.
------------------------------------------------------------------------
[2015-08-08 12:53:52] rainer dot jung at kippdata dot de
I added debug statements to zend_strtod(). The call to it gets the correct input string, and if I
print the result before returning with fprintf(stderr, "%f") it is also correct. Only the
var_dump() output float(...) is wrong. So it seems the problem is more about the formatting of float
in var_dump().
------------------------------------------------------------------------
[2015-08-08 12:41:47] rainer dot jung at kippdata dot de
Concerning zend_strtod():
- I first tried the original library available under www.netlib.org/fp. It didn't show the
problem.
- I then tried to shrink the delta between the PHP copy and the original and ended up with a test
case, that doesn't show the problem even with PHP zend_strtod(). So it seems it is something in
PHP before the calls to zend_strtod(), or the problem is in the output rendering of the float.
Here's my test:
1) Create a file dtoa-main.c containing:
#include <stdio.h>
#include <zend_strtod.h>
int main() {
fprintf(stdout, "%f\n", zend_strtod("29000000", NULL));
fprintf(stdout, "%f\n", zend_strtod("290000000", NULL));
fprintf(stdout, "%f\n", zend_strtod("2900000000", NULL));
fprintf(stdout, "%f\n", zend_strtod("29000000000", NULL));
fprintf(stdout, "%f\n", zend_strtod("290000000000", NULL));
fprintf(stdout, "%f\n", zend_strtod("2900000000000", NULL));
fprintf(stdout, "%f\n", zend_strtod("29000000000000", NULL));
fprintf(stdout, "%f\n", zend_strtod("290000000000000", NULL));
fprintf(stdout, "%f\n", zend_strtod("2900000000000000", NULL));
}
The numbers are the same as in the test.php I originally use to produce the problem.
Now compile this snippet against the original zend_strtod.o object file:
solaris10.sparc apache% gcc -I /path/to/php/bldir/Zend -I /path/to/php/bldir/TSRM -I
/path/to/php/bldir -Wl,-z -Wl,nodefs -o dtoa-test dtoa-main.c
/path/to/php/bldir/Zend/.libs/zend_strtod.o
The "-z nodefs" linker argument is only needed, because the object file references the
zend_error_noreturn symbol, which is not used in this test case. I didn't want to soak in many
more object files.
Now run the binary:
./dtoa-test
29000000.000000
290000000.000000
2900000000.000000
29000000000.000000
290000000000.000000
2900000000000.000000
29000000000000.000000
290000000000000.000000
2900000000000000.000000
So the float seems to be OK.
------------------------------------------------------------------------
[2015-08-08 11:06:44] rainer dot jung at kippdata dot de
Problem still exists for 7.0.0 Beta 3.
Linux x86_64 builds are fine (as expected).
------------------------------------------------------------------------
The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
https://bugs.php.net/bug.php?id=70173
--
Edit this bug report at https://bugs.php.net/bug.php?id=70173&edit=1