Bug #70173 [Opn]: ZVAL_COPY_VALUE_EX broken for 32bit Solaris Sparc

From: 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

« previous php.bugs (#195034) next »