Skip to content

Commit a9f590b

Browse files
committedOct 1, 2018
Prevent accurate comparison of floating-point numbers
1 parent 18c0b6e commit a9f590b

File tree

3 files changed

+107
-0
lines changed

3 files changed

+107
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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\GreaterOrEqual
29+
&& !$node instanceof BinaryOp\SmallerOrEqual
30+
) {
31+
return [];
32+
}
33+
34+
$rightType = $scope->getType($node->right);
35+
$leftType = $scope->getType($node->left);
36+
37+
if ($rightType instanceof FloatType || $leftType instanceof FloatType) {
38+
if ($node instanceof BinaryOp\Equal || $node instanceof BinaryOp\Identical) {
39+
return [
40+
'You can not use an exact comparison for floating-point numbers.
41+
Should use `abs($left - $right) < $epsilon`, where $epsilon maximum permissible error.',
42+
];
43+
}
44+
if ($node instanceof BinaryOp\GreaterOrEqual) {
45+
return [
46+
'You can not use an exact comparison for floating-point numbers.
47+
Should use `$left - $right >= $epsilon`, where $epsilon maximum permissible error.',
48+
];
49+
}
50+
if ($node instanceof BinaryOp\SmallerOrEqual) {
51+
return [
52+
'You can not use an exact comparison for floating-point numbers.
53+
Should use `$right - $left >= $epsilon`, where $epsilon maximum permissible error.',
54+
];
55+
}
56+
}
57+
58+
return [];
59+
}
60+
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
'You can not use an exact comparison for floating-point numbers.
20+
Should use `abs($left - $right) < $epsilon`, where $epsilon maximum permissible error.',
21+
113,
22+
],
23+
[
24+
'You can not use an exact comparison for floating-point numbers.
25+
Should use `abs($left - $right) < $epsilon`, where $epsilon maximum permissible error.',
26+
114,
27+
],
28+
[
29+
'You can not use an exact comparison for floating-point numbers.
30+
Should use `$left - $right >= $epsilon`, where $epsilon maximum permissible error.',
31+
115,
32+
],
33+
[
34+
'You can not use an exact comparison for floating-point numbers.
35+
Should use `$right - $left >= $epsilon`, where $epsilon maximum permissible error.',
36+
116,
37+
],
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)
Failed to load comments.