Edit report at https://bugs.php.net/bug.php?id=70173&edit=1
ID: 70173
Comment by: rainer dot jung at kippdata dot de
Reported by: rainer dot jung at kippdata dot de
Summary: Precision problem in float
Status: Open
Type: Bug
Package: *General Issues
Operating System: Solaris 10 Sparc
PHP Version: 7.0.0beta2
Block user comment: N
Private report: N
New Comment:
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.
Previous Comments:
------------------------------------------------------------------------
[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).
------------------------------------------------------------------------
[2015-08-03 13:06:41] cmb@php.net
That might be related to the update of Zend/zend_strtod*[1].
[1] <https://github.com/php/php-src/commit/5d4616e2b08f20ce6a582587cf9d3e34775a43a4>
------------------------------------------------------------------------
[2015-07-31 05:53:27] rainer dot jung at kippdata dot de
Description:
------------
A precision problem in float makes a huge number of tests fail. It could well be, that this is a
platform specific failure (Solaris 10 Sparc, 32 Bit build).
Below you will find a test case with correct result returned from version 5.6.11 and wrong result
from 7.0.0 Beta 2.
Test script:
---------------
<?php
$var = 29000000;
var_dump($var);
$var = 290000000;
var_dump($var);
$var = 2900000000;
var_dump($var);
$var = 29000000000;
var_dump($var);
$var = 290000000000;
var_dump($var);
$var = 2900000000000;
var_dump($var);
$var = 29000000000000;
var_dump($var);
$var = 290000000000000;
var_dump($var);
$var = 2900000000000000;
var_dump($var);
?>
Expected result:
----------------
int(29000000)
int(290000000)
float(2900000000)
float(29000000000)
float(290000000000)
float(2900000000000)
float(29000000000000)
float(2.9E+14)
float(2.9E+15)
Actual result:
--------------
int(29000000)
int(290000000)
float(2899998720)
float(28999991296)
float(289999945728)
float(2899998408704)
float(28999988281344)
float(2.899999499223E+14)
float(2.8999984254812E+15)
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=70173&edit=1