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

Commit 4f11909

Browse files
Prevent exact comparison of floating-point numbers
1 parent 18c0b6e commit 4f11909

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\BinaryOp;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Type\FloatType;
10+
11+
class OperandsInComparisonRule implements Rule
12+
{
13+
14+
public function getNodeType(): string
15+
{
16+
return BinaryOp::class;
17+
}
18+
19+
/**
20+
* @param Node $node
21+
* @param Scope $scope
22+
* @return string[]
23+
*/
24+
public function processNode(Node $node, Scope $scope): array
25+
{
26+
if (!$node instanceof BinaryOp\Equal
27+
&& !$node instanceof BinaryOp\Identical
28+
&& !$node instanceof BinaryOp\NotEqual
29+
&& !$node instanceof BinaryOp\NotIdentical
30+
&& !$node instanceof BinaryOp\GreaterOrEqual
31+
&& !$node instanceof BinaryOp\SmallerOrEqual
32+
) {
33+
return [];
34+
}
35+
36+
$rightType = $scope->getType($node->right);
37+
$leftType = $scope->getType($node->left);
38+
39+
if ($rightType instanceof FloatType || $leftType instanceof FloatType) {
40+
if ($node instanceof BinaryOp\Equal || $node instanceof BinaryOp\Identical) {
41+
return [
42+
'Exact comparison of floating-point numbers is not accurate.' . PHP_EOL
43+
. 'You should use `abs($left - $right) < $epsilon`, where $epsilon is maximum allowed deviation.',
44+
];
45+
}
46+
47+
if ($node instanceof BinaryOp\NotEqual || $node instanceof BinaryOp\NotIdentical) {
48+
return [
49+
'Exact comparison of floating-point numbers is not accurate.' . PHP_EOL
50+
. 'You should use `abs($left - $right) >= $epsilon`, where $epsilon is maximum allowed deviation.',
51+
];
52+
}
53+
54+
if ($node instanceof BinaryOp\GreaterOrEqual) {
55+
return [
56+
'Exact comparison of floating-point numbers is not accurate.' . PHP_EOL
57+
. 'You should use `$left - $right >= $epsilon`, where $epsilon is maximum allowed deviation.',
58+
];
59+
}
60+
if ($node instanceof BinaryOp\SmallerOrEqual) {
61+
return [
62+
'Exact comparison of floating-point numbers is not accurate.' . PHP_EOL
63+
. 'You should use `$right - $left >= $epsilon`, where $epsilon is maximum allowed deviation.',
64+
];
65+
}
66+
}
67+
68+
return [];
69+
}
70+
71+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Rules\Rule;
6+
7+
class OperandsInComparisonRuleTest extends \PHPStan\Testing\RuleTestCase
8+
{
9+
10+
protected function getRule(): Rule
11+
{
12+
return new OperandsInComparisonRule();
13+
}
14+
15+
public function testRule(): void
16+
{
17+
$this->analyse([__DIR__ . '/data/operators.php'], [
18+
[
19+
'Exact comparison of floating-point numbers is not accurate.' . PHP_EOL
20+
. 'You should use `abs($left - $right) < $epsilon`, where $epsilon is maximum allowed deviation.',
21+
113,
22+
],
23+
[
24+
'Exact comparison of floating-point numbers is not accurate.' . PHP_EOL
25+
. 'You should use `abs($left - $right) < $epsilon`, where $epsilon is maximum allowed deviation.',
26+
114,
27+
],
28+
[
29+
'Exact comparison of floating-point numbers is not accurate.' . PHP_EOL
30+
. 'You should use `abs($left - $right) >= $epsilon`, where $epsilon is maximum allowed deviation.',
31+
115,
32+
],
33+
[
34+
'Exact comparison of floating-point numbers is not accurate.' . PHP_EOL
35+
. 'You should use `abs($left - $right) >= $epsilon`, where $epsilon is maximum allowed deviation.',
36+
116,
37+
],
38+
[
39+
'Exact comparison of floating-point numbers is not accurate.' . PHP_EOL
40+
. 'You should use `$left - $right >= $epsilon`, where $epsilon is maximum allowed deviation.',
41+
117,
42+
],
43+
[
44+
'Exact comparison of floating-point numbers is not accurate.' . PHP_EOL
45+
. 'You should use `$right - $left >= $epsilon`, where $epsilon is maximum allowed deviation.',
46+
118,
47+
],
48+
]);
49+
}
50+
51+
}

‎tests/Rules/Operators/data/operators.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,10 @@ function (array $array, int $int, $mixed) {
109109

110110
explode($mixed, $mixed) + $int;
111111
};
112+
113+
$float === 123.2;
114+
$float == 123.2;
115+
$float !== 123.2;
116+
$float != 123.2;
117+
$float >= 123.2;
118+
$float <= 123.2;

0 commit comments

Comments
(0)

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