Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Prevent accurate comparison of floating-point numbers #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
dmytro-dymarchuk wants to merge 1 commit into phpstan:1.4.x
base: 1.4.x
Choose a base branch
Loading
from dmytro-dymarchuk:prevent-accurate-comparison-of-floating-point-numbers

Conversation

Copy link

@dmytro-dymarchuk dmytro-dymarchuk commented Oct 1, 2018
edited
Loading

Prevent situation when comparison return can unexpected result. For example:

php > $a = 0.15 + 0.15;
php > $b = 0.1 + 0.2;
php > $c = 0.3;
php > var_dump($a === $b);
bool(false)
php > var_dump($b == $c);
bool(false)
php > var_dump($b <= $c);
bool(false)

Majkl578, mhujer, and CodeDuck42 reacted with thumbs up emoji JanTvrdik reacted with thumbs down emoji
@dmytro-dymarchuk dmytro-dymarchuk force-pushed the prevent-accurate-comparison-of-floating-point-numbers branch 2 times, most recently from 460df80 to a9f590b Compare October 1, 2018 17:47
@dmytro-dymarchuk dmytro-dymarchuk force-pushed the prevent-accurate-comparison-of-floating-point-numbers branch from a9f590b to 4f11909 Compare October 1, 2018 18:57
@dmytro-dymarchuk dmytro-dymarchuk force-pushed the prevent-accurate-comparison-of-floating-point-numbers branch from 4f11909 to 07e5b84 Compare October 1, 2018 19:14
Copy link

JanTvrdik commented Oct 2, 2018
edited
Loading

Just a note: abs($left - $right) < $epsilon does not always work. See for example nette/tester#68


Edit: Although i guess it depends on how you compute $epsilon. If you scale it with the $left and $right it should work just fine.

Copy link

jasny commented Nov 20, 2018

@ondrejmirtes Here a realistic case where this would cause an issue.

function isPaid(array $amounts, array $payments)
{
 $total = array_sum($amounts);
 $paid = array_sum($payments);
 return $paid >= $total;
}
$amounts = [0.1, 0.2];
$payments = [0.15, 0.15];
var_dump(isPaid($amounts, $payments)); // false

Copy link
Contributor

adaamz commented Feb 12, 2019

Copy link
Contributor

@CzechBoy Forbidding floats altogether and avoiding errorneous comparison by equality are two different use cases.

Copy link
Contributor

adaamz commented Feb 12, 2019

@Majkl578 sure, I only send link to similiar library.

Copy link
Member

Hi, I'm not sure about this one, I worry it'd be too annoying. AFAIK floats are precise to a certain number of decimals so for example if you compare two floats after rounding them to one decimal place (like 1.3) then comparing them with === is fine.

But feel free to publish this as a separate package, I'm certain some people would like it. I'm gonna wait some time before closing for feedback from the others.

Copy link
Contributor

Majkl578 commented Aug 3, 2020

@ondrejmirtes

AFAIK floats are precise to a certain number of decimals so for example if you compare two floats after rounding them to one decimal place (like 1.3) then comparing them with === is fine.

The (need of / absence of) rounding in first place is the problem here as it's something people usually don't do / forget.
E.g.:

php > var_dump(.1 + .2 == .3);
bool(false)

IMO PHPStan should warn about this code, possibly directly in core, not only in phpstan-strict-rules. 🤔

https://andy-carter.com/blog/don-t-trust-php-floating-point-numbers-when-equating

Copy link
Member

The linked article shows that $test == 0.3 can be valid code but it would be marked with an error by this PR.

Copy link
Contributor

The linked article shows that $test == 0.3 can be valid code but it would be marked with an error by this PR.

Not sure bcadd(0.1, 0.2, 1); should a real recommended way.
Most of the time you'll sum variable bcadd($a, $b, 1);, then you don't know how much significative numbers is needed because 0.3001 and 0.3 should not be considered the same.

I find an article recommending (string) $test == '0.3' to avoid issue with float comparison.

Copy link
Contributor

Prevent situation when comparison return can unexpected result. For example:

php > $a = 0.15 + 0.15;
php > $b = 0.1 + 0.2;
php > $c = 0.3;
php > var_dump($a === $b);
bool(false)
php > var_dump($b == $c);
bool(false)
php > var_dump($b <= $c);
bool(false)

@dmytro-dymarchuk What about the comparison with 0.0 ?

Can we have unexpected behavior with code like

$float === 0.0;
$float !== 0.0;
$float >= 0.0;
$float <= 0.0;

Or should it be a special case ignored by your rule ?

Copy link
Contributor

dereuromark commented Nov 23, 2023
edited
Loading

$floatAmount1 = 0.1;
$floatAmount2 = 0.2;
$sumFloat = $floatAmount1 + $floatAmount2;
echo "Sum using float: $sumFloat\n"; // Output: Sum using float: 0.30000000000000004
$stringAmount1 = '0.1';
$stringAmount2 = '0.2';
$sumString = bcadd($stringAmount1, $stringAmount2, 1);
echo "Sum using string: $sumString\n"; // Output: Sum using string: 0.3

So yeah, either bcmath and strings, or better yet a value object:
https://github.com/php-collective/decimal-object

Copy link

Having caused myself some serious headaches with float comparisons that work in some situations but not others, I'd be very much behind this being included here. I don't see it as being any more annoying than requiring booleans in conditionals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Reviewers
1 more reviewer

@Majkl578 Majkl578 Majkl578 left review comments

Reviewers whose approvals may not affect merge requirements
Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /