Skip to content

fix(parser): classify auto-Decimal literals by real mantissa precision - #50

Merged
matyhtf merged 1 commit into
swoole:masterfrom
AlessioGiacobbe:split/decimal-literal-classification
Sep 1, 2026
Merged

fix(parser): classify auto-Decimal literals by real mantissa precision#50
matyhtf merged 1 commit into
swoole:masterfrom
AlessioGiacobbe:split/decimal-literal-classification

Conversation

@AlessioGiacobbe

Copy link
Copy Markdown
Contributor

The auto-Decimal reclassification of float literals (≥16 significant digits → php::Decimal, per docs/en/HIGH_PRECISION_TYPES.md) misfired three ways: exponent digits and trailing zeros counted as significant (is_float(2.220446049250313E-16) compiled to false, 1.23456789012345e300 became Decimal), overflowing hex literals containing the digit E became Decimal("0x…") instead of folding to the exact double, and valid PHP produced by var_export() round-trips — 0.1 + 0.2 == 0.30000000000000004 — failed to compile with "Cannot convert float expression to Decimal".

Classification now counts real mantissa precision with a round-trip-exactness check, excludes hex/octal/binary literals, and demotes a Decimal-classified bare literal to its exact double when it meets a float-typed expression.

Verified against Zend 8.4.13; tests + phpt included.

Part of the split of #39.

The >=16-significant-digit float-literal-to-php::Decimal promotion
(docs/en/HIGH_PRECISION_TYPES.md) counted every digit in the raw
literal with preg_replace('/[^0-9]/'), so exponent digits and trailing
zeros counted as significant: 1.23456789012345e300 (15 significant
digits) and 999999999999999.0 became Decimal, making
is_float(2.220446049250313E-16) compile to false. Hex literals whose
digits contain E (0x123456789E1234567) matched the [.eE] test and
became Decimal("0x..."), where Zend folds an overflowing hex literal
to its exact double.

Three fixes, keeping the documented feature:
- Count true mantissa significant digits (strip sign, exponent,
  leading and trailing zeros) and additionally require that the double
  cannot reproduce the literal exactly - a literal that round-trips
  (every var_export/serialize output, PHP_FLOAT_EPSILON) has lost
  nothing and stays float, while 3.14159265358979323846 still promotes.
- Exclude hex/octal/binary notation from the reclassification.
- When a Decimal-classified literal meets a float-typed expression in
  a binary op or comparison, demote the literal to its exact double
  instead of the "Cannot convert float expression to Decimal" fatal:
  PHP evaluates every float literal as a double, so
  0.1 + 0.2 == 0.30000000000000004 is valid PHP and must be true.
@matyhtf
matyhtf merged commit 407953c into swoole:master Sep 1, 2026
14 checks passed
@AlessioGiacobbe
AlessioGiacobbe deleted the split/decimal-literal-classification branch September 1, 2026 13:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants