php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #75644 round(362.42499999999995, 2) gives incorrect result
Submitted: 2017-12-06 22:47 UTC Modified: 2021-07-14 17:12 UTC
Votes:6
Avg. Score:4.7 ± 0.7
Reproduced:6 of 6 (100.0%)
Same Version:2 (33.3%)
Same OS:3 (50.0%)
From: markamery at btinternet dot com Assigned: cmb (profile)
Status: Wont fix Package: Math related
PHP Version: 7.1.12 OS: Ubuntu
Private report: No CVE-ID: None
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please — but make sure to vote on the bug!
Your email address:
MUST BE VALID
Solve the problem:
47 - 13 = ?
Subscribe to this entry?

 
 [2017-12-06 22:47 UTC] markamery at btinternet dot com
Description:
------------
Even though 362.42499999999995 is less than 362.425 (i.e. the internally-stored IEEE-754 representation of 362.42499999999995 has a value less than 362.425), round(362.42499999999995, 2) gives 362.43

It should give 362.42

Test script:
---------------
<?php
$x = 362.42499999999995;
echo $x . "\n";
echo round($x, 2) . "\n";
ini_set('precision', 999);
echo $x . "\n";
echo round($x, 2) . "\n";


Expected result:
----------------
362.425
362.42
362.424999999999954525264911353588104248046875
362.42000000000001591615728102624416351318359375


Actual result:
--------------
362.425
362.43
362.424999999999954525264911353588104248046875
362.43000000000000682121026329696178436279296875


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-07-09 15:40 UTC] cmb@php.net
-Status: Open +Status: Wont fix -Assigned To: +Assigned To: cmb
 [2021-07-09 15:40 UTC] cmb@php.net
TIL that PHP uses an elaborate rounding algorithm[1], and due to
this, you get the wrong result.  Changing this would require an
RFC (and the RFC author would need to figure out a way how to
support other modes than HALF_EVEN), but fortunately the float to
string conversion has not been "fixed", so you can use e.g.
printf() to get the desired results.

Anyhow, while rounding to two decimals might have practical use
cases, monetary arithmetic is not one of them.

[1] <https://wiki.php.net/rfc/rounding>
 [2021-07-10 11:38 UTC] markamery at btinternet dot com
Oh, wow. So, if I'm understanding correctly, the cause of this behaviour is that round() *deliberately* does "pre-rounding", where it rounds the float you give it to 15 significant figures first, and *then* rounds the result of *that* step to the number of decimal places you ask for. The motivating idea seems to be to treat floats as if they were decimal numbers with 15 significant figures of precision, and treat the subsequent decimal digits as being nonsense.

(On top of that, the rounding to 15 sig figs is itself done in a dubious way, where the value is multiplied up by a power of 10 to get 15 sig figs before the decimal point, then rounded to an integer. But that multiplication can change what the 15th significant digit will come out as when you round. For instance, 362.42499999999949 to 15 SF should be 362.424999999999, but because 362.42499999999949 * 100000000000000 gives 36242499999999952, flipping the 16th sig fig from a 4 to a 5, we instead get 362.425000000000.)

I'm not even sure how one would go about clearly documenting this function's behaviour as it currently stands. What guarantees do we have? Rounding direction will be correct as long as the value is no more than 0.5 * 10^-14 away from the midpoint, maybe? Or that if you parse a decimal number containing no more than 15 digits to a float, then round it, you'll get the result that is correct for the decimal value you parsed? I'm not confident that even those things are guaranteed to be honest.
 [2021-07-14 17:12 UTC] cmb@php.net
> I'm not confident that even those things are guaranteed to be
> honest.

Me neither.  I think the initial mistake was to introduce the
$precision parameter (there is a reason C doesn't support that),
and the follow-up mistake was to try to make round() appear to
work on precise decimal numbers.  Probably too late to revert
either mistake. :(
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Mar 28 19:01:29 2024 UTC