From 743c170e3970f89294d376ff7a3c97230c8749a1 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 26 May 2026 18:08:16 +0800 Subject: [PATCH] =?UTF-8?q?docs(native=5Ftypes):=20=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E4=B8=AD=E9=AB=98=E7=B2=BE=E5=BA=A6=E6=95=B0?= =?UTF-8?q?=E5=80=BC=E7=B1=BB=E5=9E=8B=E4=BD=BF=E7=94=A8=E6=8C=87=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 BigInt、Decimal、BigFloat 三种高精度数值类型的详细介绍 - 补充高精度数值类型的 C++ 底层库依赖和关键头文件说明 - 详细说明高精度数值类型的声明与构造方式 - 完善算术运算符和比较运算符的使用示例 - 添加通用方法表格对比 BigInt、Decimal、BigFloat 的方法差异 - 补充类型转换规则和跨类型转换限制说明 - 添加 C++ API 参考和超长字面量自动识别机制说明 - 增加使用建议章节区分原生类型与 ZVAL 的适用场景 --- docs/NATIVE_TYPES.md | 245 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 244 insertions(+), 1 deletion(-) diff --git a/docs/NATIVE_TYPES.md b/docs/NATIVE_TYPES.md index 3a25017c..da4b6e9a 100644 --- a/docs/NATIVE_TYPES.md +++ b/docs/NATIVE_TYPES.md @@ -139,7 +139,9 @@ $obj = objval(new MyClass(), 'MyClass'); // 不需要 | **时机** | 运行时优化 | 编译期推断 | | **语法** | `std::int(值)` | `objval(变量,'类名')` | ----## ❌ 不支持的类型 +--- + +## ❌ 不支持的类型 以下类型**不使用**原生类型,仍然使用 ZVAL: @@ -196,6 +198,247 @@ function process(string $name, array $data) { } ``` +## 使用建议 + +### ✅ 推荐使用原生类型的场景 +- 数值密集计算 +- 循环计数器 +- 递归算法 +- 性能关键路径 + +### ⚠️ 使用 ZVAL 的场景 +- 字符串处理 +- 数组操作 +- 对象操作 +- 通用业务逻辑 + +--- + +## 高精度数值类型:BigInt / Decimal / BigFloat + +AOT 编译器支持三种高精度数值类型,用于处理超出 int64/double 精度的计算。 + +### 底层 C++ 库 + +| 类型 | C++ 库 | 关键头文件 | +|------|--------|----------| +| **BigInt** | GMP (`libgmp-dev`) | ``, `phpx_big_int.h` | +| **Decimal** | libmpdec (`libmpdec-dev`) | ``, `phpx_decimal.h` | +| **BigFloat** | MPFR (`libmpfr-dev`) | ``, `phpx_big_float.h` | + +BigInt、Decimal、BigFloat 均继承自 `php::Box`,存储于 `php::Variant` 内部。它们属于"装箱类型"(Boxed Type),不像 Int/Float 那样直接映射为 C++ 标量,因此在声明和运算上有所不同。 + +### 声明与构造 + +```php +use native_types; + +// 从整数字面量构造 BigInt +$a = std::bigInt(100); +$b = std::bigInt("123456789012345678901234567890"); // 超长整数字符串 + +// 从字符串构造 Decimal(避免浮点精度丢失) +$c = std::decimal("123.456"); +$d = std::decimal(42); // 也可从 int 构造 + +// 从 int / float / 字符串构造 BigFloat +$e = std::bigFloat(100.5); +$f = std::bigFloat(42); +$g = std::bigFloat("3.14159265358979323846"); +``` + +> **重要**:`std::bigInt()` / `std::decimal()` / `std::bigFloat()` 是**编译期函数**,在生成的 C++ 代码中直接构造对应类型,无运行时函数调用开销。 + +### 算术运算符 + +所有标准二元运算符均已重载:`+`、`-`、`*`、`/`、`%`(取模)、`**`(幂运算)。编译器将其映射为静态方法调用。 + +```php +$a = std::bigInt(100); +$b = std::bigInt(200); + +$sum = $a + $b; // → php::BigInt::add($a, $b) +$diff = $a - $b; // → php::BigInt::sub($a, $b) +$prod = $a * $b; // → php::BigInt::mul($a, $b) +$quot = $a / $b; // → php::BigInt::div($a, $b) +$mod = $a % $b; // → php::BigInt::mod($a, $b) +$pow = $a ** 3; // → php::BigInt::pow($a, 3) + +// 一元取负 +$neg = -$a; // → php::BigInt::neg($a) +``` + +**类型提升**:当 Big* 类型与 Int/Float 混合运算时,Int/Float 自动提升为对应的高精度类型。详见下文"二元运算类型提升规则"。 + +**BigInt 除法**:`BigInt / BigInt` 在 `parseBinaryOp` 中返回 BigInt(整数除法,同 PHP int 语义)。若需要高精度除法,应先将操作数转为 Decimal 或使用 `BigInt::div` 的 Decimal 结果。 + +### 比较运算符 + +所有标准比较运算符均可使用:`<`、`>`、`<=`、`>=`、`==`、`!=`、`<=>`(太空船)。 + +```php +$a = std::bigInt(100); +$b = 200; + +echo (int)($a < $b); // → php::BigInt::cmp($a, $b) < 0 +echo (int)($a > $b); // → php::BigInt::cmp($a, $b) > 0 +echo (int)($a == 100); // → php::BigInt::cmp($a, 100) == 0 +echo (int)($a <=> $b); // → php::BigInt::cmp($a, $b) +``` + +C++ 实现:比较结果通过 `php::BigInt::cmp()` / `php::Decimal::cmp()` / `php::BigFloat::cmp()` 获得,返回负/零/正 int 表示小于/等于/大于。 + +### 通用方法 (Universal Methods) + +BigInt、Decimal、BigFloat 支持通过 `$value->method()` 语法调用一系列零成本抽象方法。 + +#### BigInt 方法 + +| 方法 | 参数 | 返回类型 | C++ 实现 | 说明 | +|------|------|---------|---------|------| +| `add($x)` | 1 | BigInt | `BigInt::add()` | 加法 | +| `sub($x)` | 1 | BigInt | `BigInt::sub()` | 减法 | +| `mul($x)` | 1 | BigInt | `BigInt::mul()` | 乘法 | +| `div($x)` | 1 | BigInt | `BigInt::div()` | 整数除法 | +| `mod($x)` | 1 | BigInt | `BigInt::mod()` | 取模 | +| `pow($x)` | 1 | BigInt | `BigInt::pow()` | 幂运算 | +| `neg()` | 0 | BigInt | `BigInt::neg()` | 取负 | +| `abs()` | 0 | BigInt | `BigInt::abs()` | 绝对值 | +| `gcd($x)` | 1 | BigInt | `BigInt::gcd()` | 最大公约数 | +| `cmp($x)` | 1 | Int | `BigInt::cmp()` | 比较 | +| `toString()` | 0 | Str | `BigInt::toString()` | 转字符串 | +| `toInt()` | 0 | Int | `BigInt::toInt()` | 转整数 (可能截断) | +| `toFloat()` | 0 | Float | `BigInt::toFloat()` | 转浮点 (可能丢精度) | + +```php +$a = std::bigInt("12345678901234567890"); +echo $a->toString(); // "12345678901234567890" +echo $a->add(1)->toString(); // "12345678901234567891" +echo $a->abs()->toString(); // "12345678901234567890" +echo $a->gcd(15)->toInt(); // 15 +``` + +#### Decimal 方法 + +| 方法 | 参数 | 返回类型 | C++ 实现 | 说明 | +|------|------|---------|---------|------| +| `add($x)` | 1 | Decimal | `Decimal::add()` | 加法 | +| `sub($x)` | 1 | Decimal | `Decimal::sub()` | 减法 | +| `mul($x)` | 1 | Decimal | `Decimal::mul()` | 乘法 | +| `div($x)` | 1 | Decimal | `Decimal::div()` | 除法 | +| `mod($x)` | 1 | Decimal | `Decimal::mod()` | 取模 | +| `neg()` | 0 | Decimal | `Decimal::neg()` | 取负 | +| `abs()` | 0 | Decimal | `Decimal::abs()` | 绝对值 | +| `cmp($x)` | 1 | Int | `Decimal::cmp()` | 比较 | +| `toString()` | 0 | Str | `Decimal::toString()` | 转字符串 | +| `toInt()` | 0 | Int | `Decimal::toInt()` | 截断取整 | +| `toFloat()` | 0 | Float | `Decimal::toFloat()` | 转浮点 (约 15 位精度) | + +```php +$d = std::decimal("123.456"); +echo $d->toInt(); // 123 +echo $d->mul(2)->toString(); // "246.912" +echo $d->div(3)->toString(); // "41.152" +``` + +#### BigFloat 方法 + +| 方法 | 参数 | 返回类型 | C++ 实现 | 说明 | +|------|------|---------|---------|------| +| `add($x)` | 1 | BigFloat | `BigFloat::add()` | 加法 | +| `sub($x)` | 1 | BigFloat | `BigFloat::sub()` | 减法 | +| `mul($x)` | 1 | BigFloat | `BigFloat::mul()` | 乘法 | +| `div($x)` | 1 | BigFloat | `BigFloat::div()` | 除法 | +| `neg()` | 0 | BigFloat | `BigFloat::neg()` | 取负 | +| `abs()` | 0 | BigFloat | `BigFloat::abs()` | 绝对值 | +| `cmp($x)` | 1 | Int | `BigFloat::cmp()` | 比较 | +| `toString()` | 0 | Str | `BigFloat::toString()` | 转字符串 | +| `toInt()` | 0 | Int | `BigFloat::toInt()` | 截断取整 | +| `toFloat()` | 0 | Float | `BigFloat::toFloat()` | 转 double (约 15 位精度) | + +```php +$bf = std::bigFloat(3.14159265); +echo $bf->mul(2)->toString(); // "6.2831853..." +echo $bf->div(2)->toFloat(); // 1.570796325 +echo $bf->cmp(3.0); // > 0 +``` + +### 类型转换 + +```php +// BigInt → Decimal(精确) +$big = std::bigInt("12345678901234567890"); +$dec = std::decimal($big->toString()); +// 或直接使用内置转换: +// $dec = $big->toDecimal(); // 待实现 + +// Decimal → BigInt(截断小数部分) +$d = std::decimal("123.456"); +$i = std::bigInt($d->toInt()); // 123 + +// Int → BigInt +$bi = std::bigInt(42); + +// Float → BigFloat +$bf = std::bigFloat(3.14); + +// BigInt → BigFloat +$bf2 = std::bigFloat($big->toString()); +``` + +> **跨类型隐式转换限制**:BigFloat 与 BigInt/Decimal 之间不能隐式混合运算。编译器会报错提示使用 `std::bigFloat()` 显式转换。这是为了防止意外的精度损失。 + +### C++ API 参考 + +Big* 类型在 `phpx` 库中提供以下核心函数: + +```cpp +// 构造 +php::Variant php::newBigInt(const std::string &s); +php::Variant php::newBigInt(php::Int v); +php::Variant php::newDecimal(const String &s); +php::Variant php::newDecimal(php::Int v); +php::Variant php::newBigFloat(const String &s); +php::Variant php::newBigFloat(php::Int v); +php::Variant php::newBigFloat(php::Float v); + +// BigInt 算术(均返回 Variant) +php::BigInt::add(a, b) php::BigInt::sub(a, b) php::BigInt::mul(a, b) +php::BigInt::div(a, b) php::BigInt::mod(a, b) php::BigInt::pow(a, b) +php::BigInt::neg(a) php::BigInt::abs(a) php::BigInt::gcd(a, b) +php::BigInt::cmp(a, b) + +// Decimal 算术 +php::Decimal::add(a, b) php::Decimal::sub(a, b) php::Decimal::mul(a, b) +php::Decimal::div(a, b) php::Decimal::mod(a, b) +php::Decimal::neg(a) php::Decimal::abs(a) php::Decimal::cmp(a, b) + +// BigFloat 算术 +php::BigFloat::add(a, b) php::BigFloat::sub(a, b) php::BigFloat::mul(a, b) +php::BigFloat::div(a, b) +php::BigFloat::neg(a) php::BigFloat::abs(a) php::BigFloat::cmp(a, b) + +// 类型转换 +php::BigInt::toString(a) php::BigInt::toInt(a) php::BigInt::toFloat(a) +php::Decimal::toString(a) php::Decimal::toInt(a) php::Decimal::toFloat(a) +php::BigFloat::toString(a) php::BigFloat::toInt(a) php::BigFloat::toFloat(a) +``` + +所有静态方法接收 `Variant` 参数,内部通过 `.toBox()` / `.toBox()` / `.toBox()` 提取底层对象。若参数类型不匹配,抛出运行时错误。 + +### 超长字面量识别 + +AOT 编译器在解析前会对 PHP 源码进行预扫描,自动识别超出 int64/double 精度的数值字面量: + +``` +\d{19,} → 自动转为 std::bigInt("...") +\d+\.\d{16,} → 自动转为 std::decimal("...") +``` + +例如源码中写 `123456789000000000000000000000000000000000000000000001`(54 位),编译器自动处理为 `std::bigInt("123456789000000000000000000000000000000000000000000001")`,无需手动包装。 + +--- + ## 二元运算类型提升规则 AOT 编译器在执行 `+`、`-`、`*`、`/`、`%` 等二元运算时,按以下优先级确定运算类型: