Bug #75644 [Wfx]: round(362.42499999999995, 2) gives incorrect result
Edit report at https://bugs.php.net/bug.php?id=75644&edit=1
ID: 75644
Updated by: cmb@php.net
Reported by: markamery at btinternet dot com
Summary: round(362.42499999999995, 2) gives incorrect result
Status: Wont fix
Type: Bug
Package: Math related
Operating System: Ubuntu
PHP Version: 7.1.12
Assigned To: cmb
Block user comment: N
Private report: N
New Comment:
> 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. :(
Previous Comments:
------------------------------------------------------------------------
[2021-07-10 11:38:07] 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-09 15:40:17] 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>
------------------------------------------------------------------------
[2017-12-06 22:47:34] 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
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=75644&edit=1
Thread (4 messages)