php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #54479 round(1e15+0.1) returns 1e15+0.1 instead of 1e15
Submitted: 2011-04-06 19:22 UTC Modified: 2011-04-17 03:02 UTC
From: for-bugs at hnw dot jp Assigned:
Status: Not a bug Package: Math related
PHP Version: 5.3.6 OS: any
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: for-bugs at hnw dot jp
New email:
PHP Version: OS:

 

 [2011-04-06 19:22 UTC] for-bugs at hnw dot jp
Description:
------------
When round() is called with 1 argument which value is between 1e15 and 2^53, round() returns non-rounded value even if the value has fractional part.

For instance, though 1e15 is exact number as IEEE 754 double precision, round(1e15+0.1) returns 1e15+0.1. I think 1e15 is better result.

Test script:
---------------
<?php
'ini_set("precision",18);
var_dump(round(1000000000000000.1));

Expected result:
----------------
float(1000000000000000)

Actual result:
--------------
float(1000000000000000.12)

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-04-06 19:39 UTC] derick@php.net
-Status: Open +Status: Bogus
 [2011-04-06 19:39 UTC] derick@php.net
Floating point values have a limited precision. Hence a value might 
not have the same string representation after any processing. That also
includes writing a floating point value in your script and directly 
printing it without any mathematical operations.

If you would like to know more about "floats" and what IEEE
754 is, read this:
http://www.floating-point-gui.de/

Thank you for your interest in PHP.

.
 [2011-04-07 01:52 UTC] for-bugs at hnw dot jp
My test script seems to be wrong. How about next one?


<?php
ini_set("precision",19);
var_dump(1000000000000000.125);
// float(1000000000000000.125)
var_dump(round(1000000000000000.125));
// PHP 5.3.6 returns float(1000000000000000.125)
// PHP 5.2.17 returns float(1000000000000000)


IEEE 754 double precision have 53 bit fractions, so 1000000000000000 and 1000000000000000.125 is exact number.


This behavior is caused by ext/standard/math.c:160-163

    /* This value is beyond our precision, so rounding it is pointless */
    if (fabs(tmp_value) >= 1e15) {
      return value;
    }
 [2011-04-07 14:19 UTC] cataphract@php.net
If anything, the cutoff value is too high:

The accuracy (effective number of digits to the right of the decimal point) of a double is given by 15.9546-log10(|x|) and:

In[22]:= Solve[$MachinePrecision-Log[10, Abs[x]]==1.,x]
                         14
Out[22]= {{x -> 9.0072 10  }}
 [2011-04-10 21:57 UTC] uncorrelated at yahoo dot co dot jp
I'm suprized that floating point numbers of PHP doesn't support for IEEE 754. I'd like to confirm wheather PHP 5.3 supports for IEEE 754 or not.

IEEE 754 ensures that 1000000000000000 differs from 1000000000000000.125 (see appendix). Then, IEEE 754 requires round(1000000000000000.125) returns 1000000000000000. In fact, the function of PHP 5.2.17 returns 1000000000000000.

In spite of that, the function of PHP 5.3.6 returns 1000000000000000.125. It seems to be strange. If it is NOT a bug, PHP 5.3 doesn't support IEEE 754.
Mr hnw may think that PHP should support for IEEE 754 as long as the system allows it, and the cutoff (precious) value shouldn't affect any operation but printing. 

Appendix:

(Decimal)1000000000000000.125 is represented as the followings:
Bits of Sign: 0 
Bits of Exponent: 10000110000 
Bits of Significand: 1100011010111111010100100110001101000000000000000001

(Decimal)1000000000000000 is represented as the followings:
Bits of Sign: 0 
Bits of Exponent: 10000110000
Bits of Significand: 1100011010111111010100100110001101000000000000000000

The significand of 1000000000000000.125 isn't same as that of 1000000000000000 (See the right edges of them).
 [2011-04-16 20:39 UTC] exploringbinary at gmail dot com
I would think since 1000000000000000.125 is exactly representable (53 bits), round(1000000000000000.125,3) would return 1000000000000000.125; but on PHP 5.3.5, it returns 1.0E+15. Compare this to printf("%.3f",1000000000000000.125), which prints 1000000000000000.125.(See http://bugs.php.net/bug.php?id=53918 for discussion on printing exactly representable values.)
 [2011-04-17 03:02 UTC] cataphract@php.net
PHP simplifies the round implementation by giving up more or less around when the accuracy falls below one. This is indeed a bit lazy even though the units digit is not accurate; without checking the algorithm/history maybe the purpose is to avoid the algorithm doesn't terminate due to adding numbers >=1 having no effect in the result.

In any case, feel free to propose a patch.
 [2011-04-17 15:11 UTC] exploringbinary at gmail dot com
I said round(1000000000000000.125,3) returns 1.0E+15, but that's not true: ECHO returns that. round() seems to work as I expect for this example if I show it with printf:

<?php echo round(1000000000000000.125,3); // Prints 1.0E+15 ?>

<?php printf("%.3f",round(1000000000000000.125,3)); // Prints 1000000000000000.125 ?>


(Interestingly, printf("%.3f",round(1000000000000000.125,0)) prints 1000000000000000.125)
 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Sun Apr 05 01:01:24 2020 UTC