You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Incorrect round($num, 0, PHP_ROUND_HALF_UP) result for $num = 1.4999999999999998 / 4503599627370495.5 #12143
Extracted from #12056 (comment) into a dedicated issue. The solution consists of using modf() and then explicitly checking against 0.5, instead of adding or subtracting 0.5, since the result of the addition / subtraction might not be exactly representable.
/cc @jorgsowa Are you interested in developing the fix?
Those numbers you tested cannot be exactly represented as double-precision IEEE-754 floating point numbers and thus are internally rounded to the nearest representable number which is either 0.49999999999999994 or 0.5.
This issue is about the fact that 0.4999999999999999 + 0.5 is not exactly representable and thus internally rounded to 1.0, thus floor(float + 0.5) as the current implementation of PHP_ROUND_HALF_UP takes the floor(1.0) and erroneously returns 1.0 instead of the correct result 0.0.
The round function implicitly uses + 0.5 before creating the file patch
When I write this into the function round(0.499999999999999944, 0); with or without a constant PHP_ROUND_HALF_UP the result 1 is incorrect since the first digit is 4 and therefore not equal to 5 or greater
Trying to fix this problem, I tried debugging with GDB, and it turned out to be a surprisingly messy process.
Honestly, I think it's a miracle that it's working now.
If I'm not mistaken, the entire round() function needs modification.
Folks, the cause of this issue is clear and the necessary fix is already outlined in the initial issue: Use modf() and explicitly compare the decimal part to 0.5.
I'd write a fix myself, but as @jorgsowa is currently working on an RFC for round() I did not want to trample on his feet and leave him the opportunity to develop the fix, as the issue was detected during review of his RFC implementation.
Yeah I agree that it's unreliable and that's why I think we can just strip it because that's what user asked for. Doing nothing seems worse to me really. The error is already there just by saving the number and converting it to double. The round will just do what requested.
Thank you @TimWolla for handling this issue using modf. It's reasonable to me and probably even faster than my solution. I'm away now for 2 weeks and I will not be able to contribute much this time, so that's why I'm little "silent" last days.
changed the title [-]Incorrect `round($num, 0, PHP_ROUND_HALF_UP)` result for `$num = 0.49999999999999994 / 1.4999999999999998 / 4503599627370495.5`[/-][+]Incorrect `round($num, 0, PHP_ROUND_HALF_UP)` result for `$num = 1.4999999999999998 / 4503599627370495.5`[/+]on Sep 19, 2023
Activity
bukka commentedon Sep 7, 2023
Yeah this seems like a bug to me.
jorgsowa commentedon Sep 7, 2023
TBH, I have no idea how to fix this and whether it counts into the floating point precision problem.
I also found out that displaying this number and numbers of close range is problematic.
https://3v4l.org/PdcJW
Result:
jorgsowa commentedon Sep 7, 2023
I prepared test case for set of similar numbers and it completely gives up with rounding: https://3v4l.org/A12Z0
TimWolla commentedon Sep 7, 2023
Those numbers you tested cannot be exactly represented as double-precision IEEE-754 floating point numbers and thus are internally rounded to the nearest representable number which is either
0.49999999999999994or0.5.This issue is about the fact that
0.4999999999999999 + 0.5is not exactly representable and thus internally rounded to1.0, thusfloor(float + 0.5)as the current implementation ofPHP_ROUND_HALF_UPtakes thefloor(1.0)and erroneously returns1.0instead of the correct result0.0.SakiTakamachi commentedon Sep 7, 2023
It's only morning in Japan, so I'm really sorry if I'm just half asleep.
Apparently,
PHP_ROUND_HALF_DOWNseems to be worse.https://3v4l.org/0rmFT
It's probably a combination of subtraction and rounding error.
hormus commentedon Sep 8, 2023
Hello good morning everyone, using the number_format function you can extend the unrepresented decimals. Storing floats is often lossy in precision.
Expected result:
The round function implicitly uses + 0.5 before creating the file patch
When I write this into the function round(0.499999999999999944, 0); with or without a constant PHP_ROUND_HALF_UP the result 1 is incorrect since the first digit is 4 and therefore not equal to 5 or greater
SakiTakamachi commentedon Sep 8, 2023
Trying to fix this problem, I tried debugging with GDB, and it turned out to be a surprisingly messy process.
Honestly, I think it's a miracle that it's working now.
If I'm not mistaken, the entire round() function needs modification.
TimWolla commentedon Sep 8, 2023
Folks, the cause of this issue is clear and the necessary fix is already outlined in the initial issue: Use
modf()and explicitly compare the decimal part to0.5.I'd write a fix myself, but as @jorgsowa is currently working on an RFC for
round()I did not want to trample on his feet and leave him the opportunity to develop the fix, as the issue was detected during review of his RFC implementation.SakiTakamachi commentedon Sep 8, 2023
@TimWolla
Unfortunately, it wasn't that simple.
Please see my issue.
#12153
TimWolla commentedon Sep 8, 2023
@SakiTakamachi The cause of your issue is identical to this issue, you just used different numbers to show the problem.
23 remaining items
SakiTakamachi commentedon Sep 15, 2023
@TimWolla
Certainly, using
modf()seems reasonable.bukka commentedon Sep 15, 2023
Yeah I agree that it's unreliable and that's why I think we can just strip it because that's what user asked for. Doing nothing seems worse to me really. The error is already there just by saving the number and converting it to double. The
roundwill just do what requested.SakiTakamachi commentedon Sep 15, 2023
I see.
If you think accuracy is important, you can use it in a way that prioritizes accuracy yourself, and it may be a good thing to have more options.
Reimplement `php_round_helper()` using `modf()`
jorgsowa commentedon Sep 15, 2023
Thank you @TimWolla for handling this issue using
modf. It's reasonable to me and probably even faster than my solution. I'm away now for 2 weeks and I will not be able to contribute much this time, so that's why I'm little "silent" last days.TimWolla commentedon Sep 15, 2023
@jorgsowa Thank you for the acknowledgement.
Reimplement `php_round_helper()` using `modf()` (#12220)
[-]Incorrect `round($num, 0, PHP_ROUND_HALF_UP)` result for `$num = 0.49999999999999994 / 1.4999999999999998 / 4503599627370495.5`[/-][+]Incorrect `round($num, 0, PHP_ROUND_HALF_UP)` result for `$num = 1.4999999999999998 / 4503599627370495.5`[/+]TimWolla commentedon Sep 19, 2023
I've dropped 0.49999999999999994 from the title, because that one is fixed now.
round: Bypass the precision logic when rounding to 0 places
Fix phpGH-12143: Optimize round