Skip to content

fix(compiler): make numeric string arithmetic match PHP semantics - #136

Open
alwaysLinger wants to merge 1 commit into
swoole:masterfrom
alwaysLinger:fix/float-numeric-string-arithmetic
Open

alwaysLinger wants to merge 1 commit into
swoole:masterfrom
alwaysLinger:fix/float-numeric-string-arithmetic

Conversation

@alwaysLinger

Copy link
Copy Markdown

问题

同一算术表达式,数字串字面量形态与变量形态行为不一致——变量形态正确,字符串形态偏离:

var_dump("1e2" / 3);
$y = "1.5" * 2;
var_dump($y);
PHP(正确行为)    float(33.333333333333336)  float(3)
TypePHP 修复前     int(33)                    int(3)    ← 数值被静默截断
TypePHP 修复后     float(33.333333333333336)  float(3)  ← 与 PHP 一致

PHP 手册规定:纯十进制整数且在 int64 范围内的数字串("5"、"10")按 int 参与算术,其余数字串("1e2"、"1.5")按 float。此偏离不在官方不兼容清单内。

根因

三条路径都未把数字串字面量当作数值操作数:

  • 类型推断:src/CompilerBase.php detectTypeOfExpr() 算术分支,一侧为原生 Int 时无条件返回 Type::INT——赋值目标被声明为 php::Int 槽位,double 值写入即截断。
  • 代码生成:src/CompilerBase.php parseNumericIdentifier() 用 (string) floatval("1e2") 转换数字串,得到 "100"(无小数点),发出的 C++ 是整型字面量。
  • 常量求值:src/Parser/BinaryOpTrait.php constantNumericValue() 不处理 Node\Scalar\String_,字符串派生的常量绕过溢出检查与折叠。

修复

  • 新增 isIntNumericString() / isFloatNumericString()(src/Parser/TypeDetectionTrait.php):is_numeric + 纯整数语法正则 + int64 范围检查,与 PHP 数字串分类语义一致。
  • detectTypeOfExpr():一侧为浮点数字串时,+ - * / ** 结果提升为 Type::FLOAT(%/移位/位运算排除);varint 模式下整型语法数字串视同 int 操作数参与常量求值。
  • parseNumericIdentifier():浮点数字串经仓库现有 shortestFloatRepr() 发射,保留 C++ double 形态(1e+2);溢出为 INF 时退回运行时字符串路径。
  • constantNumericValue():读取数字串字面量(整型语法 → intval,其余有限数字串 → floatval)。现有常量溢出防护因此覆盖字符串常量:native 模式下 "9223372036854775807" * 2 与 int 字面量写法一致地被编译期拒绝;varint 模式下折叠为 PHP float。

行为变化

  • native:"9223372036854775807" * 2 从“编译通过、运行时未定义行为”变为编译期拒绝——与 int 字面量写法一致。
  • varint:整型数字串溢出从“错误 float”折叠为正确 PHP float;"5" / 2 从 int(2) 修正为 float(2.5)(官方文档 varint_types 职责:“溢出转浮点与小数除法”)。
  • bigint:"9223372036854775807" * 2 从运行时崩溃(ValueError: Invalid BigInt numeric string)修复为精确 BigInt 运算。

保持不变

  • 整型语法数字串的 native 语义:"10" / 2 → int(5)、"5" + 1 → int(6)——逐字节与修复前一致。
  • 变量路径($f = "1e2"; $f / 3)原本正确,未触碰。
  • %/移位/位运算不涉及。

验证

Linux x86-64 Docker,PHP 8.4.26 NTS,GCC:

  • 约 70 例行为矩阵与 php 基准逐字节对拍(含大写 E、负指数、.5/5.、首尾空白、INF、int64 边界、幂/比较/移位/位运算/复合赋值),native 与 varint 双模式,-O0/-O2/-O3 一致;use bigint_types 独立对拍:修复前运行时崩溃、修复后精确 BigInt。
  • 残余差异仅 3 例,均为下方披露的范围外项,修复前后行为相同。
  • 测试有效性:stash 补丁后 PHPT 与 phpunit 断言均变 FAIL,恢复后 PASS。
  • 回归面:tests/compiler/{operator,basic,std-array,class} 188 项无新增失败(唯一失败 pow-int-overflow 在未修改基线上同样失败,为容器 precision=14 环境差异);全量 phpunit 2409 项仅 2 例失败(容器缺 python3,基线相同)。

…heir arithmetic matches PHP semantics across all modes
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.

1 participant