diff --git a/src/Type/Constant/ConstantFloatType.php b/src/Type/Constant/ConstantFloatType.php index 4a8d6f8501..e595f55b8c 100644 --- a/src/Type/Constant/ConstantFloatType.php +++ b/src/Type/Constant/ConstantFloatType.php @@ -15,9 +15,9 @@ use PHPStan\Type\Type; use PHPStan\Type\VerbosityLevel; use function abs; +use function ini_get; +use function ini_set; use function is_finite; -use function rtrim; -use function sprintf; use function strpos; use const PHP_FLOAT_EPSILON; @@ -45,18 +45,27 @@ public function equals(Type $type): bool return $type instanceof self && abs($this->value - $type->value) < PHP_FLOAT_EPSILON; } + private function castFloatToString(float $value): string + { + $precisionBackup = ini_get('precision'); + ini_set('precision', '-1'); + try { + $valueStr = (string) $value; + if (is_finite($value) && strpos($valueStr, '.') === false) { + $valueStr .= '.0'; + } + + return $valueStr; + } finally { + ini_set('precision', $precisionBackup); + } + } + public function describe(VerbosityLevel $level): string { return $level->handle( static fn (): string => 'float', - function (): string { - $formatted = (string) $this->value; - if (is_finite($this->value) && strpos($formatted, '.') === false) { - $formatted .= '.0'; - } - - return $formatted; - }, + fn (): string => $this->castFloatToString($this->value), ); } @@ -110,7 +119,7 @@ public function generalize(GeneralizePrecision $precision): Type */ public function toPhpDocNode(): TypeNode { - return new ConstTypeNode(new ConstExprFloatNode(rtrim(rtrim(sprintf('%.20f', $this->value), '0'), '.'))); + return new ConstTypeNode(new ConstExprFloatNode($this->castFloatToString($this->value))); } /** diff --git a/tests/PHPStan/Type/Constant/ConstantFloatTypeTest.php b/tests/PHPStan/Type/Constant/ConstantFloatTypeTest.php index 00ec37a8b6..2122e3c829 100644 --- a/tests/PHPStan/Type/Constant/ConstantFloatTypeTest.php +++ b/tests/PHPStan/Type/Constant/ConstantFloatTypeTest.php @@ -23,6 +23,14 @@ public function dataDescribe(): array new ConstantFloatType(1.2000000992884E-10), '1.2000000992884E-10', ], + [ + new ConstantFloatType(-1.200000099288476E+10), + '-12000000992.88476', + ], + [ + new ConstantFloatType(-1.200000099288476E+20), + '-1.200000099288476E+20', + ], [ new ConstantFloatType(1.2 * 1.4), '1.68', diff --git a/tests/PHPStan/Type/TypeToPhpDocNodeTest.php b/tests/PHPStan/Type/TypeToPhpDocNodeTest.php index 1f3b55a31b..69bef029f1 100644 --- a/tests/PHPStan/Type/TypeToPhpDocNodeTest.php +++ b/tests/PHPStan/Type/TypeToPhpDocNodeTest.php @@ -18,6 +18,8 @@ use PHPStan\Type\Generic\GenericObjectType; use stdClass; use function sprintf; +use const PHP_INT_MAX; +use const PHP_INT_MIN; class TypeToPhpDocNodeTest extends PHPStanTestCase { @@ -311,24 +313,59 @@ public function dataToPhpDocNodeWithoutCheckingEquals(): iterable '(literal-string & non-falsy-string)', ]; + yield [ + new ConstantIntegerType(PHP_INT_MIN), + (string) PHP_INT_MIN, + ]; + + yield [ + new ConstantIntegerType(PHP_INT_MAX), + (string) PHP_INT_MAX, + ]; + yield [ new ConstantFloatType(9223372036854775807), - '9223372036854775808', + '9.223372036854776E+18', ]; yield [ new ConstantFloatType(-9223372036854775808), - '-9223372036854775808', + '-9.223372036854776E+18', ]; yield [ new ConstantFloatType(2.35), - '2.35000000000000008882', + '2.35', ]; yield [ new ConstantFloatType(100), - '100', + '100.0', + ]; + + yield [ + new ConstantFloatType(8.202343767574732), + '8.202343767574732', + ]; + + yield [ + new ConstantFloatType(1e80), + '1.0E+80', + ]; + + yield [ + new ConstantFloatType(-5e-80), + '-5.0E-80', + ]; + + yield [ + new ConstantFloatType(0.0), + '0.0', + ]; + + yield [ + new ConstantFloatType(-0.0), + '-0.0', ]; }