rfc:change_the_edge_case_of_round

This is an old revision of the document!


PHP RFC: Change the edge case of round()

Introduction

round() can specify the operating mode when edge case values ​​such as HALF_UP and HALF_DOWN are passed. For example, 0.285 is internally 0.28499999999999998. Should this be considered an edge case? Or is it not an edge case? Regarding this issue, in 2008, the following RFC adopted the policy that 0.285 is considered an edge case. https://wiki.php.net/rfc/rounding

However, there is an opinion that it is wrong to expect decimal-like behavior because FP is just FP. Since 0.28499999999999995 to 0.28500000000000000 are all the same IEEE754 internal representation, and most of their values ​​are less than 0.285, it is unreasonable to treat them as edge cases.

FP is not a decimal number

When we treat FP “like” a decimal number, it is not an exact value, but an approximation with some error range. The following six numbers are all 3fd23d70a3d70a3d in IEEE754 representation.

0.28499999999999995
0.28499999999999996
0.28499999999999997
0.28499999999999998
0.28499999999999999
0.28500000000000000

Since this range does contain 0.28500000000000000, we could consider it to be 0.28500000000000000, but that would ignore the fact that the remaining five values ​​are less than 0.285.

And for users who want to round 0.28499999999999998 correctly, such behavior is a bug.

round() is the only one that handles FP like this

Now look at the following code.

var_dump(0.1 + 0.2 === 0.3);

You'll soon see that this becomes false. However, treating FP as a decimal number means that the above code must be true. I think you can understand that is strange. There is no reason why only round() should behave as a decimal.

Expecting FP to behave like "FP"

Even if we treat FP like a decimal number, we should use the value that is closest to it, and not a value that is “just within the range” that is on the boundary with the next value.

And if we expect FP to behave like a decimal number in round(), we should expect similar behavior for all FPs in PHP. We need to avoid inconsistent implementations where only certain features behave exceptionally.

If we want to calculate as a decimal number

See warnings on this page. https://www.php.net/manual/en/language.types.float.php

It can be seen that PHP's policy is not to require FP to behave as a decimal number. And as it says in the warning, if we want to calculate as a decimal number, we should use BCMath.

Proposal

This RFC proposes changes that will cause round() to stop expecting decimal behavior to FP and start expecting FP to behave as FP.

// previous behavior
var_dump(round(0.285, 2)); // float(0.29)

// new behavior
var_dump(round(0.285, 2)); // float(0.28)

Backward Incompatible Changes

Regarding edge case determination for round(), some values ​​will no longer be determined as edge cases.

Proposed PHP Version(s)

next PHP 8.x (Currently the target is 8.4)

RFC Impact

To SAPIs

None.

To Existing Extensions

Only “standard” is affected.

To Opcache

No impact.

New Constants

None.

php.ini Defaults

None.

Open Issues

None.

Unaffected PHP Functionality

There is no effect on anything other than “standard”.

Future Scope

None.

Proposed Voting Choices

We will probably vote for or against adding these functions. This requires 2/3 majority.

Implementation

References

Rejected Features

None.

rfc/change_the_edge_case_of_round.1699434841.txt.gz · Last modified: 2023/11/08 09:14 by saki