Skip to content

fix(generator): normalize float literal emission and handle INF/NAN constants - #33

Merged
matyhtf merged 1 commit into
swoole:masterfrom
prateekbhujel:prateek/fix-float-literal-codegen
Aug 30, 2026
Merged

fix(generator): normalize float literal emission and handle INF/NAN constants#33
matyhtf merged 1 commit into
swoole:masterfrom
prateekbhujel:prateek/fix-float-literal-codegen

Conversation

@prateekbhujel

@prateekbhujel prateekbhujel commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Consolidates float literal generation into Utils::genFloatLiteral() so all float-to-C++ codegen paths handle precision, locale independence, and special values consistently.

Fixes #31
Fixes #34

Details

  1. genCValue() and gen_stub.php (EvaluatedValue::getCExpr()) previously stringified floats directly, which:
  2. Switched from %g to PHP's locale-independent %h specifier (%.17h) to avoid comma decimal separators under European locales.
  3. genFloatLiteral() is now the single public helper under TypePhp\Generator\Utils, mapping special values to std::numeric_limits<double> and preserving .0 on whole numbers.
  4. Removed dead $floatPrecision property from CompilerBase.
  5. Added test coverage in OperatorTest for runtime expressions, class constant/property declaration metadata, and locale independence.

@prateekbhujel
prateekbhujel force-pushed the prateek/fix-float-literal-codegen branch from 81f93bd to e9aa012 Compare August 30, 2026 02:39

@matyhtf matyhtf left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for consolidating the float literal generation paths. The overall direction is correct, and the new tests cover ordinary runtime expression lowering well.

I found two implementation issues that need to be addressed before merging.

  1. The persistent declaration path in gen_stub.php still emits floats through strval().

EvaluatedValue::getCExpr() currently contains:

} elseif ($this->type->isInt() or $this->type->isFloat()) {
    return strval($this->value);
}

As a result, class constants and property defaults still generate raw special-value tokens:

ZVAL_DOUBLE(&const_POSITIVE_value, INF);
ZVAL_DOUBLE(&const_NEGATIVE_value, -INF);
ZVAL_DOUBLE(&const_NOT_A_NUMBER_value, NAN);

Finite values in the same path also remain dependent on the host precision setting. With precision=14, M_E still generates:

ZVAL_DOUBLE(&const_E_value, 2.718281828459);
ZVAL_DOUBLE(&property_e_default_value, 2.718281828459);

This means the PR does not yet completely fix either #31 or #34. Please route float values emitted by EvaluatedValue::getCExpr() through the same canonical float literal formatter. This may require making the formatter accessible to gen_stub.php, or moving it to a shared stateless helper.

  1. sprintf('%.17g', ...) is locale-dependent and can silently generate incorrect C++.

PHP's %g conversion uses the decimal separator from LC_NUMERIC. For example, under the available en_DK.utf8 locale:

sprintf('%.17g', 1.5) // "1,5"

The PR then generates:

return php::toFloat(1,5.0);

This is parsed as a C++ comma expression and evaluates to 5.0, so it is a semantic miscompile rather than only a syntax issue.

Please use PHP's locale-independent %h conversion instead, for example:

$text = sprintf('%.17h', $value);

The existing exponent/decimal preservation logic can remain the same.

After consolidation, CompilerBase::$floatPrecision also appears to have no remaining readers. Removing it would avoid retaining dead configuration, though this cleanup is not a merge blocker.

I reproduced both blocking cases on the PR head. The current focused PHPUnit tests pass, but they do not exercise declaration metadata or a locale with a comma decimal separator.

@matyhtf

matyhtf commented Aug 30, 2026

Copy link
Copy Markdown
Member

On a personal note, I was deeply saddened to hear about the devastating flash floods and landslides affecting Nepal and communities along the Nepal–China border.

My heartfelt condolences go to everyone who has lost family members, friends, and loved ones. My thoughts are also with those who remain missing, those who have been injured or displaced, and the rescue workers and volunteers facing extraordinarily difficult conditions.

I sincerely hope that you, your family, and your friends are safe. May those who lost their lives rest in peace, and may the affected communities find strength, support, and recovery in the difficult days ahead.

…onstants

Consolidate float-to-C++ literal generation into Utils::genFloatLiteral().

Previously:
- genCValue() stringified floats directly via (string), emitting 'INF', '-INF', 'NAN', or losing the floating-point decimal point for whole numbers like 1.0 -> '1'.
- BinaryOpTrait::genFloatLiteral() used sprintf('%.17g') without handling INF or NAN.

Now all float code generation paths delegate to Utils::genFloatLiteral(), mapping INF/-INF/NAN to std::numeric_limits<double> and ensuring whole numbers retain .0.
@prateekbhujel
prateekbhujel force-pushed the prateek/fix-float-literal-codegen branch from e9aa012 to 2d81626 Compare August 30, 2026 04:30
@prateekbhujel

Copy link
Copy Markdown
Contributor Author

Thank you @matyhtf for the thorough review and catch!

I have addressed all the feedback in the latest update:

  1. Stub declaration path in gen_stub.php: Routed float expressions in EvaluatedValue::getCExpr() through getTranslator()->genFloatLiteral(). Class constants and persistent property defaults now correctly emit std::numeric_limits<double> tokens and full 17-digit precision without host ini dependency.
  2. Locale-independence: Switched from %.17g to PHP's locale-independent %.17h format specifier.
  3. Dead code cleanup: Removed the unused $floatPrecision property from CompilerBase.
  4. Test coverage: Added dedicated test cases in OperatorTest covering declaration metadata (_arginfo.h constants and extension property defaults) and locale independence under comma-decimal locales.

And thank you so much for your kind words and thoughts regarding the situation in Nepal. It means a lot. Everyone in our family is safe. Wishing you all the best as well!

@prateekbhujel
prateekbhujel requested a review from matyhtf August 30, 2026 04:32

@matyhtf matyhtf left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for addressing all previous review findings. I verified that the persistent declaration path now uses the canonical formatter, the locale-independent float format is used, and the dead precision configuration has been removed. I also compiled the new declaration fixture with precision=14; the generated C++ builds and runs successfully.

There is a small isolation issue in the new locale test: setlocale returns the newly selected locale rather than the previous one, so the test does not restore the original locale. This does not affect the generator fix, and we will correct the test on master in a follow-up commit. The implementation itself is approved.

@matyhtf
matyhtf merged commit d0ff8c5 into swoole:master Aug 30, 2026
@prateekbhujel
prateekbhujel deleted the prateek/fix-float-literal-codegen branch August 30, 2026 07:41
@prateekbhujel

prateekbhujel commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for the merge and the follow-up locale fix, the LC_NUMERIC-only approach with the skip guard is much cleaner than what I had. Appreciate you taking the time to tidy that up.

Really glad this contribution made it in. Looking forward to digging deeper into the codebase and contributing more where I can.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants