Skip to content

Commit 460df80

Browse files
Prevent accurate comparison of floating-point numbers
1 parent 18c0b6e commit 460df80

File tree

3 files changed

+108
-0
lines changed

3 files changed

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

tests/Rules/Operators/data/operators.php

+5
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,8 @@ 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;

0 commit comments

Comments
 (0)