Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions phpunit/code/float-declaration-metadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

class FloatDeclarationContainer
{
public const float POSITIVE_INF = INF;
public const float NEGATIVE_INF = -INF;
public const float NOT_A_NUMBER = NAN;
public const float CONST_E = M_E;
public const float CONST_ONE_POINT_FIVE = 1.5;

public float $property_e = M_E;
public float $property_inf = INF;
public float $property_nan = NAN;
public float $property_one_point_five = 1.5;
}

function main(): void
{
}
13 changes: 13 additions & 0 deletions phpunit/code/float-literal-special.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

function get_special_floats(): array
{
return [
1.0,
0.0,
INF,
-INF,
NAN,
M_E,
];
}
4 changes: 2 additions & 2 deletions phpunit/src/CompilerBaseApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ public function testWindowsIntegerLiteralSuffixForGeneratedCValues(): void

public function testGeneratedCValuesAreAlwaysSourceCodeStrings(): void
{
$this->assertSame((string) M_E, $this->invokeMethod('genCValue', M_E));
$this->assertSame(M_E, (float) $this->invokeMethod('genCValue', M_E));
$this->assertSame('1', $this->invokeMethod('genCValue', true));
$this->assertSame('0', $this->invokeMethod('genCValue', false));

Expand All @@ -370,7 +370,7 @@ public function testGeneratedCValuesAreAlwaysSourceCodeStrings(): void
new \PhpParser\Node\Expr\ConstFetch(new \PhpParser\Node\Name('M_E'))
);
$this->assertIsString($code);
$this->assertSame((string) M_E, $code);
$this->assertSame(M_E, (float) $code);
}

public function testNumericStringIdentifiersGenerateSourceCodeStrings(): void
Expand Down
3 changes: 2 additions & 1 deletion phpunit/src/Generator/UtilsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public function testGenCValueInt(): void
public function testGenCValueFloat(): void
{
$result = $this->invokeMethod('genCValue', 3.14);
$this->assertSame((string) 3.14, $result);
$this->assertSame(3.14, (float) $result);
$this->assertMatchesRegularExpression('/[.E]/i', $result);
}

public function testGenCValueBool(): void
Expand Down
77 changes: 77 additions & 0 deletions phpunit/src/OperatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,81 @@ public function testLiteralModuloAssignByZeroDoesNotCompile(): void
{
$this->exec('Cannot divide or modulo by zero', 'assign-modulo-by-zero.php');
}

public function testFloatLiteralSpecialValuesAndWholeNumbers(): void
{
$previous = ini_set('precision', '14');
try {
global $translator;
$compiler = \TypePhp\CompilerTest::create(TYPEPHP_ROOT_PATH);
$translator = $compiler;
$testFile = __DIR__ . '/../code/float-literal-special.php';
$compiler->addFiles([$testFile]);
$compiler->prepareFile($testFile);
$cppFile = $compiler->convertFile($testFile);
$cpp = file_get_contents($cppFile);
} finally {
if ($previous !== false) {
ini_set('precision', $previous);
}
}

$this->assertStringContainsString('1.0', $cpp);
$this->assertStringContainsString('0.0', $cpp);
$this->assertStringContainsString('std::numeric_limits<double>::infinity()', $cpp);
$this->assertStringContainsString('-std::numeric_limits<double>::infinity()', $cpp);
$this->assertStringContainsString('std::numeric_limits<double>::quiet_NaN()', $cpp);
$this->assertStringContainsString('2.7182818284590451', $cpp);
$this->assertStringNotContainsString('2.718281828459)', $cpp);
}

public function testFloatDeclarationMetadataIgnoresHostPrecisionAndHandlesSpecialValues(): void
{
$previous = ini_set('precision', '14');
try {
global $translator;
$compiler = \TypePhp\CompilerTest::create(TYPEPHP_ROOT_PATH);
$translator = $compiler;
$testFile = TYPEPHP_ROOT_PATH . '/phpunit/code/float-declaration-metadata.php';
$compiler->addFiles([$testFile]);
$compiler->prepareFile($testFile);
$compiler->convertFile($testFile);
$arginfoHeader = $compiler->getArgInfoHeaderFile($testFile);
$arginfo = file_get_contents($arginfoHeader);
$extension = file_get_contents($compiler->genExtension());
} finally {
if ($previous !== false) {
ini_set('precision', $previous);
}
}

$this->assertStringContainsString('ZVAL_DOUBLE(&const_POSITIVE_INF_value, std::numeric_limits<double>::infinity());', $arginfo);
$this->assertStringContainsString('ZVAL_DOUBLE(&const_NEGATIVE_INF_value, -std::numeric_limits<double>::infinity());', $arginfo);
$this->assertStringContainsString('ZVAL_DOUBLE(&const_NOT_A_NUMBER_value, std::numeric_limits<double>::quiet_NaN());', $arginfo);
$this->assertStringContainsString('ZVAL_DOUBLE(&const_CONST_E_value, 2.7182818284590451);', $arginfo);
$this->assertStringContainsString('ZVAL_DOUBLE(&const_CONST_ONE_POINT_FIVE_value, 1.5);', $arginfo);
$this->assertStringNotContainsString('2.718281828459);', $arginfo);

$this->assertStringContainsString('php::toFloat(2.7182818284590451)', $extension);
$this->assertStringContainsString('php::toFloat(std::numeric_limits<double>::infinity())', $extension);
$this->assertStringContainsString('php::toFloat(std::numeric_limits<double>::quiet_NaN())', $extension);
$this->assertStringNotContainsString('2.718281828459)', $extension);
}

public function testFloatLiteralEmissionIsLocaleIndependent(): void
{
$previousLocale = setlocale(LC_ALL, 'de_DE.UTF-8', 'da_DK.UTF-8', 'en_DK.utf8');
try {
global $translator;
$compiler = \TypePhp\CompilerTest::create(TYPEPHP_ROOT_PATH);
$translator = $compiler;
$result = $compiler->genFloatLiteral(1.5);
$this->assertSame('1.5', $result);
$this->assertStringNotContainsString(',', $result);
} finally {
if ($previousLocale !== false) {
setlocale(LC_ALL, $previousLocale);
}
}
}
}
14 changes: 1 addition & 13 deletions src/CompilerBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,6 @@ protected function getBoolValue(Expr\ConstFetch $expr): string
protected array $linkPaths = []; // --link-path / -L: user-specified library search paths
/** @var list<string> Required PHP modules recorded in zend_module_entry.deps. */
protected array $extensionDependencies = [];
protected int $floatPrecision = 17;
protected bool $debug = false;
protected bool $formatCode = false; // --format: enable clang-format (disabled by default)
protected bool $printBacktraceOnError = true;
Expand Down Expand Up @@ -4246,18 +4245,7 @@ protected function parseInclude(Expr\Include_ $expr): string

protected function parseScalarFloat(Node\Scalar\Float_ $expr): string
{
$value = $expr->value;

if (is_nan($value)) {
return self::VALUE_NAN;
}
if (is_infinite($value)) {
return $value > 0 ? self::VALUE_INF : '-' . self::VALUE_INF;
}
if (floor($value) == $value && abs($value) < 1e15) {
return number_format($value, 1, '.', '');
}
return sprintf('%.' . $this->floatPrecision . 'g', $value);
return $this->genFloatLiteral($expr->value);
}

protected function parseIsset(Expr\Isset_ $expr): string
Expand Down
18 changes: 17 additions & 1 deletion src/Generator/Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,29 @@ protected function genIntegerLiteral(int $value): string
return $value . $this->getPlatform()->getIntegerLiteralSuffix();
}

public function genFloatLiteral(float $value): string
{
if (is_nan($value)) {
return self::VALUE_NAN;
}
if (is_infinite($value)) {
return $value > 0 ? self::VALUE_INF : '-' . self::VALUE_INF;
}
$text = sprintf('%.17h', $value);
// Make sure the literal is parsed as a C++ double.
if (!str_contains($text, '.') && !str_contains(strtolower($text), 'e')) {
$text .= '.0';
}
return $text;
}

protected function genCValue(mixed $value): string
{
if (is_int($value)) {
return $this->genIntegerLiteral($value);
}
if (is_float($value)) {
return (string) $value;
return $this->genFloatLiteral($value);
}
if (is_bool($value)) {
return $value ? '1' : '0';
Expand Down
9 changes: 0 additions & 9 deletions src/Parser/BinaryOpTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -490,15 +490,6 @@ protected function handleNestedConstantDivisionByZero(
return '((php::Var(' . $leftExpr . ')) ' . $op . ' (php::Var(' . $rightExpr . ')))';
}

protected function genFloatLiteral(float $value): string
{
$text = sprintf('%.17g', $value);
// Make sure the literal is parsed as a C++ double.
if (!str_contains($text, '.') && !str_contains(strtolower($text), 'e')) {
$text .= '.0';
}
return $text;
}

protected function shouldMaterializeOrderedOperand(NodeAbstract $expr): bool
{
Expand Down
8 changes: 1 addition & 7 deletions src/Parser/ConstantExpressionTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,13 +236,7 @@ protected function genInternalScalarConstantValue(mixed $value): string
return $this->genIntegerLiteral($value);
}
if (is_float($value)) {
if (is_nan($value)) {
return self::VALUE_NAN;
}
if (is_infinite($value)) {
return $value > 0 ? self::VALUE_INF : '-' . self::VALUE_INF;
}
return $this->genCValue($value);
return $this->genFloatLiteral($value);
}
if (is_bool($value)) {
return $value ? 'true' : 'false';
Expand Down
4 changes: 3 additions & 1 deletion src/gen_stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -2827,8 +2827,10 @@ public function getCExpr(): ?string
// reduced constant string expressions. Emitting that value avoids
// leaking heredoc/nowdoc source syntax into generated C++.
return '"' . getTranslator()->escapeString((string) $this->value) . '"';
} elseif ($this->type->isInt() or $this->type->isFloat()) {
} elseif ($this->type->isInt()) {
return strval($this->value);
} elseif ($this->type->isFloat()) {
return getTranslator()->genFloatLiteral((float) $this->value);
} elseif ($this->type->isBool()) {
return $this->value ? 'true' : 'false';
}
Expand Down