diff --git a/docs/HIGH_PRECISION_TYPES.md b/docs/HIGH_PRECISION_TYPES.md index d13a7d31..3fde670c 100644 --- a/docs/HIGH_PRECISION_TYPES.md +++ b/docs/HIGH_PRECISION_TYPES.md @@ -1,6 +1,6 @@ # AOT 编译器高精度类型使用教程 -本教程介绍 AOT 编译器中的三种高精度数值类型——**BigInt**(任意精度整数)、**Decimal**(任意精度十进制数)和 **BigFloat**(任意精度浮点数)——帮助你编写高精度、零开销的数值计算程序。 +本教程介绍 AOT 编译器中的三种高精度数值类型——**BigInt**(任意精度整数)、**Decimal**(50 位十进制数)和 **BigFloat**(256 bit 浮点数)。 ## 目录 @@ -34,13 +34,13 @@ $a = 123456789012345678901234567890; // 30 位整数 → 被转为 float,精 $b = 0.1 + 0.2; // 0.30000000000000004 — 经典的浮点误差 ``` -AOT 编译器提供了三种高精度类型,底层基于成熟的 C/C++ 数学库,编译为本地机器码,**零运行时开销**: +AOT 编译器提供了三种高精度类型,底层基于成熟的 C/C++ 数学库,并直接生成本地调用。这里的“零成本抽象”是指没有 PHP 方法查找和解释器分派开销;高精度运算本身仍需要数学库计算、内存分配和装箱: | 类型 | 底层库 | 特点 | |------|--------|------| | BigInt | GMP (`libgmp`) | 任意精度整数,不会溢出 | | Decimal | libmpdec | 十进制小数,约 50 位有效数字,无二进制浮点误差 | -| BigFloat | MPFR (`libmpfr`) | 任意精度浮点数,可调精度 | +| BigFloat | MPFR (`libmpfr`) | 默认 256 bit,字符串输出 64 位有效数字 | --- @@ -89,7 +89,7 @@ $a = std::bigInt("1234567890123456789012345678901234567890"); // 40 位 $b = $a * 2; // 80 位,不会溢出 ``` -### Decimal — 任意精度十进制数 +### Decimal — 50 位十进制数 适用于金融计算等需要精确十进制表示的场景。`0.1 + 0.2` 精确等于 `0.3`,不存在二进制浮点误差。 @@ -99,9 +99,9 @@ $quantity = 3; $total = $price * $quantity; // 59.97,精确 ``` -### BigFloat — 任意精度浮点数 +### BigFloat — 256 bit 高精度浮点数 -适用于科学计算等需要高精度浮点运算的场景。基于 MPFR,使用二进制浮点但精度远超 IEEE 754 double。 +适用于科学计算等需要高精度浮点运算的场景。基于 MPFR,当前默认精度固定为 256 bit,远高于 IEEE 754 double 的 53 bit。 ```php $pi = std::bigFloat("3.141592653589793238462643383279502884197"); @@ -152,7 +152,7 @@ $c = std::bigFloat(3.14); // → C++: php::Variant(new BigFloat(3.14)) ### 5.1 标准运算符 -所有标准二元运算符都可以直接用于 Big* 类型: +支持的运算符取决于具体类型:BigInt 支持 `+ - * / % **`,Decimal 支持 `+ - * / %`,BigFloat 支持 `+ - * /`: ```php $a = std::bigInt(100); @@ -182,7 +182,7 @@ php::BigInt::pow(a, b) // BigInt 幂运算 ### 5.2 与 int / float 混合运算 -Big* 类型可以自由地与普通 int 和 float 混合运算,编译器自动进行类型提升: +Big* 类型可以在安全范围内与普通 int/float 混合运算,编译器自动进行类型提升: ```php $a = std::bigInt(100); @@ -315,7 +315,7 @@ $a -= 1; // ✅ 代替 $a-- ## 8. 通用方法调用 -Big* 类型支持通过 `$value->method()` 语法(通用方法/Universal Methods)调用方法。这些调用在编译时被翻译为对应的 C++ 静态函数,**零运行时开销**。 +Big* 类型支持通过 `$value->method()` 语法(通用方法/Universal Methods)调用方法。这些调用在编译时直接翻译为对应的 C++ 静态函数,没有动态方法分派开销;数学库运算、结果分配和装箱成本仍然存在。 ### 8.1 BigInt 方法 @@ -343,7 +343,7 @@ if ($a->cmp(100) > 0) { /* $a > 100 */ } // 类型转换方法 echo $a->toString(); // 转字符串:"12345678901234567890" -echo $a->toInt(); // 转 int(可能截断) +echo $a->toInt(); // 转 int;超出 PHP int 范围时抛出 ArithmeticError echo $a->toFloat(); // 转 float(可能丢精度) ``` @@ -433,12 +433,17 @@ $bf3 = std::bigFloat($big->toString()); // BigInt → 普通类型 $a = std::bigInt("99999999999999999999"); $s = $a->toString(); // "99999999999999999999" -$i = $a->toInt(); // PHP_INT_MAX(超出范围时截断) +$i = $a->toInt(); // 超出 PHP int 范围时抛出 ArithmeticError $f = $a->toFloat(); // 1.0E+20(可能丢失精度) // 普通类型 → BigInt(通过编译期函数) $b = std::bigInt(42); // int → BigInt $c = std::bigInt("123456..."); // string → BigInt + +// 强制转换和 PHP 转换函数会按数值转换,不会读取 Box resource id +$n = (int) std::decimal("12.75"); // 12 +$x = floatval(std::bigInt("42")); // 42.0 +$ok = boolval(std::bigFloat("0")); // false ``` ### 9.3 跨类型隐式混合的限制 @@ -472,17 +477,14 @@ $c = $a + std::bigFloat($b->toString()); // ✅ ## 10. 混合运算与类型提升 -当 Big* 类型与普通 Int/Float 混合运算时,编译器按优先级确定运算类型: - -``` -BigFloat > Decimal > BigInt > Float > Int -``` +当 Big* 类型与普通 Int/Float 混合运算时,编译器只执行不会改变数值模型的安全提升。 **规则**: 1. 若任一操作数是 Var(非原生类型),则全部转为 Var,使用 ZendVM 运行时运算 2. 若两操作数均为 Int/Float,则 Float 优先(Int → Float) -3. 若任一操作数为 Big* 类型,则另一操作数自动提升为同类型(Int → BigInt 等) +3. BigInt 可安全提升 Int;Decimal 可提升 Int 和保留源码文本的 Float 字面量;BigFloat 可提升 Int/Float +4. 不同 Big* 类型之间,以及 BigInt 与 Float 之间,不进行隐式转换 ```php // 类型提升示例 @@ -571,11 +573,20 @@ $c = $a + $b; // ❌ 编译错误 $c = $a + std::bigFloat($b->toString()); // ✅ ``` -### 12.6 不能在普通 PHP 解释器中运行 +该限制同样适用于比较运算。比较前必须把两边显式转换为同一种 Big* 类型,避免编译为错误的底层资源类型。 + +### 12.6 边界和异常 + +- BigInt 负数右移采用算术右移,例如 `std::bigInt("-3") >> 1` 得到 `-2`。 +- 负数 bit index、负数 `popCount()`、过大的幂指数会抛出 `ValueError`。 +- 除零抛出 `DivisionByZeroError`;转为 PHP int 时超出范围抛出 `ArithmeticError`。 +- BigFloat 的绝对值指数超过 10000 时,`toString()` 自动使用科学计数法,避免构造超大字符串。 + +### 12.7 不能在普通 PHP 解释器中运行 Big* 类型是 AOT 编译器的专有特性,依赖编译期代码生成和 C++ 底层库。源码不能被 `php` 命令直接解释执行。 -### 12.7 启用 `use native_types` +### 12.8 启用 `use native_types` 忘记添加 `use native_types` 会导致 Big* 变量被当作 Var(通用类型),失去原生类型的大部分性能优势。 diff --git a/docs/NATIVE_TYPES.md b/docs/NATIVE_TYPES.md index 7b156ed5..b347a489 100644 --- a/docs/NATIVE_TYPES.md +++ b/docs/NATIVE_TYPES.md @@ -11,8 +11,8 @@ ### 高精度数值类型 4. ✅ `std::bigInt` - 任意精度整数 (基于 GMP `mpz_class`) -5. ✅ `std::decimal` - 任意精度十进制数 (基于 libmpdec, ~50 位有效数字) -6. ✅ `std::bigFloat` - 任意精度浮点数 (基于 MPFR) +5. ✅ `std::decimal` - 50 位十进制数 (基于 libmpdec) +6. ✅ `std::bigFloat` - 256 bit 高精度浮点数 (基于 MPFR,输出 64 位有效数字) --- @@ -287,7 +287,7 @@ $g = std::bigFloat("3.14159265358979323846"); ### 算术运算符 -所有标准二元运算符均已重载:`+`、`-`、`*`、`/`、`%`(取模)、`**`(幂运算)。编译器将其映射为静态方法调用。 +BigInt 支持 `+`、`-`、`*`、`/`、`%` 和 `**`;Decimal 支持除 `**` 外的前五项;BigFloat 支持 `+`、`-`、`*`、`/`。编译器将它们映射为静态方法调用。 ```php $a = std::bigInt(100); @@ -304,7 +304,7 @@ $pow = $a ** 3; // → php::BigInt::pow($a, 3) $neg = -$a; // → php::BigInt::neg($a) ``` -**类型提升**:当 Big* 类型与 Int/Float 混合运算时,Int/Float 自动提升为对应的高精度类型。详见下文"二元运算类型提升规则"。 +**类型提升**:Big* 可以和安全的普通标量混合运算;不同 Big* 类型之间不得隐式混合,必须先显式转换。详见下文“二元运算类型提升规则”。 **BigInt 除法**:`BigInt / BigInt` 在 `parseBinaryOp` 中返回 BigInt(整数除法,同 PHP int 语义)。若需要高精度除法,应先将操作数转为 Decimal 或使用 `BigInt::div` 的 Decimal 结果。 @@ -343,7 +343,7 @@ BigInt、Decimal、BigFloat 支持通过 `$value->method()` 语法调用一系 | `gcd($x)` | 1 | BigInt | `BigInt::gcd()` | 最大公约数 | | `cmp($x)` | 1 | Int | `BigInt::cmp()` | 比较 | | `toString()` | 0 | Str | `BigInt::toString()` | 转字符串 | -| `toInt()` | 0 | Int | `BigInt::toInt()` | 转整数 (可能截断) | +| `toInt()` | 0 | Int | `BigInt::toInt()` | 转整数,越界抛出 ArithmeticError | | `toFloat()` | 0 | Float | `BigInt::toFloat()` | 转浮点 (可能丢精度) | ```php @@ -422,7 +422,7 @@ $bf = std::bigFloat(3.14); $bf2 = std::bigFloat($big->toString()); ``` -> **跨类型隐式转换限制**:BigFloat 与 BigInt/Decimal 之间不能隐式混合运算。编译器会报错提示使用 `std::bigFloat()` 显式转换。这是为了防止意外的精度损失。 +> **跨类型隐式转换限制**:BigInt、Decimal、BigFloat 之间不能隐式混合运算或比较。编译器会报错并要求先显式转换为同一类型,这是为了防止精度损失和底层 Box 类型误用。 ### C++ API 参考 @@ -483,7 +483,7 @@ AOT 编译器在执行 `+`、`-`、`*`、`/`、`%` 等二元运算时,按以 ``` BigFloat / Decimal / BigInt 参与 - → 提升到最高精度类型进行计算 + → 仅安全提升 Int/Float;不同 Big* 类型要求显式转换 ↓ 未命中 任一边为 Var @@ -527,22 +527,22 @@ $f = $d + $e; // Int + Int → int64_t 加法 > **注意**:原生类型变量在运算中**不会改变自身类型**。如 `Int += Float` 在 C++ 中执行 `int64_t += double`,结果截断为 int64_t,与 PHP 行为不同(PHP 中变量会变为 float)。这是 `use native_types` 有意为之的语义。 -### 规则三:大数类型精度提升 +### 规则三:高精度类型的安全提升 -当运算数中包含 `BigInt`、`Decimal` 或 `BigFloat` 时,按精度层级提升:`BigFloat > Decimal > BigInt > Float > Int`。 +当运算数中包含 `BigInt`、`Decimal` 或 `BigFloat` 时,只对普通标量执行明确且安全的提升。不同 Big* 类型不会按所谓“精度层级”自动转换,因为三者的数值模型不同。 | 左操作数 | 右操作数 | 结果类型 | |---------|---------|---------| -| BigInt | BigInt | BigInt(除法 `/` 得 Decimal) | -| BigInt | Decimal | Decimal | +| BigInt | BigInt | BigInt(`/` 为截断整数除法) | +| BigInt | Decimal | 编译错误,需显式转换 | | Decimal | Decimal | Decimal | -| BigFloat | BigInt | BigFloat | -| BigFloat | Decimal | BigFloat | +| BigFloat | BigInt | 编译错误,需显式转换 | +| BigFloat | Decimal | 编译错误,需显式转换 | | BigFloat | BigFloat | BigFloat | | BigInt | Int | BigInt | -| BigInt | Float | Decimal | +| BigInt | Float | 编译错误 | | Decimal | Int | Decimal | -| Decimal | Float | Decimal | +| Decimal | Float | Decimal(float 字面量按源码文本转换;变量需显式转换) | | BigFloat | Int | BigFloat | | BigFloat | Float | BigFloat | @@ -551,13 +551,13 @@ $f = $d + $e; // Int + Int → int64_t 加法 | | Int | Float | Var | BigInt | Decimal | BigFloat | |------|-----|-------|-----|--------|---------|----------| | **Int** | Int | Float | Var | BigInt | Decimal | BigFloat | -| **Float** | Float | Float | Var | Decimal | Decimal | BigFloat | +| **Float** | Float | Float | Var | 错误 | Decimal* | BigFloat | | **Var** | Var | Var | Var | Var | Var | Var | -| **BigInt** | BigInt | Decimal | Var | BigInt | Decimal | BigFloat | -| **Decimal** | Decimal | Decimal | Var | Decimal | Decimal | BigFloat | -| **BigFloat** | BigFloat | BigFloat | Var | BigFloat | BigFloat | BigFloat | +| **BigInt** | BigInt | 错误 | Var | BigInt | 错误 | 错误 | +| **Decimal** | Decimal | Decimal* | Var | 错误 | Decimal | 错误 | +| **BigFloat** | BigFloat | BigFloat | Var | 错误 | 错误 | BigFloat | -> **说明**:Var 行/列全部为 Var,因为 Var 主导规则优先级最高(除 Big* 类型外)。Big* 类型参与时,Var 退让,以高精度类型为准。 +> `Decimal*`:只允许编译器能够保留原始文本的 float 字面量;float 变量必须先显式转换。Var 行/列仍使用 ZendVM 运行时语义。 ### 复合赋值运算符 diff --git a/phpunit/code/big-numeric/bigfloat-pow-operator.php b/phpunit/code/big-numeric/bigfloat-pow-operator.php new file mode 100644 index 00000000..6b58bad0 --- /dev/null +++ b/phpunit/code/big-numeric/bigfloat-pow-operator.php @@ -0,0 +1,8 @@ +exec("Operator '**' is not supported for Decimal or BigFloat", 'big-numeric/decimal-pow-operator.php'); + } + + public function testBigFloatPowerOperatorIsRejected(): void + { + $this->exec("Operator '**' is not supported for Decimal or BigFloat", 'big-numeric/bigfloat-pow-operator.php'); + } + + public function testDifferentBigTypesCannotBeComparedImplicitly(): void + { + $this->exec( + 'Cannot compare different Big* types implicitly', + 'big-numeric/mixed-big-comparison.php' + ); + } +} diff --git a/src/CompilerBase.php b/src/CompilerBase.php index 2b0ac4d5..4c84660c 100644 --- a/src/CompilerBase.php +++ b/src/CompilerBase.php @@ -3574,7 +3574,10 @@ class CompilerBase implements PropertyAccessContext protected function parseCastDouble(mixed $expr): string { $this->assertExprCanBeUsedAsValue($expr->expr, 'cast operand'); - return $this->convertFloatExpr($this->parseIdentifier($expr->expr)); + return $this->convertFloatExpr( + $this->parseIdentifier($expr->expr), + $this->detectTypeOfExpr($expr->expr) + ); } protected function detectFuncCallReturnType(string $name): string diff --git a/src/Optimizer/FuncCallOptimizer.php b/src/Optimizer/FuncCallOptimizer.php index 2c1c865d..2d08684b 100644 --- a/src/Optimizer/FuncCallOptimizer.php +++ b/src/Optimizer/FuncCallOptimizer.php @@ -484,9 +484,9 @@ trait FuncCallOptimizer } return match ($convType) { - self::ARG_TYPE_INT => $this->convertIntExpr($parsed), - self::ARG_TYPE_FLOAT => $this->convertFloatExpr($parsed), - self::ARG_TYPE_BOOL => $this->convertBoolExpr($parsed), + self::ARG_TYPE_INT => $this->convertIntExpr($parsed, $type), + self::ARG_TYPE_FLOAT => $this->convertFloatExpr($parsed, $type), + self::ARG_TYPE_BOOL => $this->convertBoolExpr($parsed, $type), default => $parsed, }; } diff --git a/src/Parser/BinaryOpTrait.php b/src/Parser/BinaryOpTrait.php index b17f1eee..5effdfef 100644 --- a/src/Parser/BinaryOpTrait.php +++ b/src/Parser/BinaryOpTrait.php @@ -332,10 +332,17 @@ trait BinaryOpTrait $this->assertExprCanBeUsedAsValue($expr->left, 'binary operand'); $this->assertExprCanBeUsedAsValue($expr->right, 'binary operand'); $leftType = $this->detectTypeOfExpr($expr->left); - if ($leftType === Type::BIGINT) { + $rightType = $this->detectTypeOfExpr($expr->right); + if ($leftType === Type::DECIMAL || $rightType === Type::DECIMAL + || $leftType === Type::BIGFLOAT || $rightType === Type::BIGFLOAT) { + $this->fatalError($expr, "Operator '**' is not supported for Decimal or BigFloat; use pow() where supported"); + } + if ($leftType === Type::BIGINT || $rightType === Type::BIGINT) { $leftExpr = $this->parseOrderedOperand($expr->left, false); $rightExpr = $this->parseOrderedOperand($expr->right, false); - $rightType = $this->detectTypeOfExpr($expr->right); + if ($leftType !== Type::BIGINT) { + $leftExpr = $this->convertBigIntExpr($leftExpr, $leftType); + } if ($rightType !== Type::BIGINT) { $rightExpr = $this->convertBigIntExpr($rightExpr, $rightType); } @@ -518,6 +525,15 @@ trait BinaryOpTrait $leftType = $this->detectTypeOfExpr($expr->left); $rightType = $this->detectTypeOfExpr($expr->right); + $bigTypes = [Type::BIGINT, Type::DECIMAL, Type::BIGFLOAT]; + if (in_array($leftType, $bigTypes, true) && in_array($rightType, $bigTypes, true) + && $leftType !== $rightType) { + $this->fatalError( + $expr, + 'Cannot compare different Big* types implicitly; convert both operands to the same type explicitly' + ); + } + if ($leftType === Type::BIGFLOAT || $rightType === Type::BIGFLOAT) { $leftExpr = $this->parseOrderedOperand($expr->left, false); $rightExpr = $this->parseOrderedOperand($expr->right, false); diff --git a/src/Parser/TypeConversionTrait.php b/src/Parser/TypeConversionTrait.php index 6494a4bf..1845591c 100644 --- a/src/Parser/TypeConversionTrait.php +++ b/src/Parser/TypeConversionTrait.php @@ -32,8 +32,17 @@ trait TypeConversionTrait return $this->convertStringExpr($expr); } - protected function convertIntExpr(string $expr): string + protected function convertIntExpr(string $expr, string $fromType = ''): string { + $bigConversion = match ($fromType) { + Type::BIGINT => 'php::BigInt::toInt', + Type::BIGFLOAT => 'php::BigFloat::toInt', + Type::DECIMAL => 'php::Decimal::toInt', + default => null, + }; + if ($bigConversion !== null) { + return $bigConversion . '(' . $expr . ')'; + } if (!$this->isClosedExpr($expr, 'php::toInt')) { return 'php::toInt(' . $expr . ')'; } @@ -41,8 +50,17 @@ trait TypeConversionTrait return $expr; } - protected function convertFloatExpr(string $expr): string + protected function convertFloatExpr(string $expr, string $fromType = ''): string { + $bigConversion = match ($fromType) { + Type::BIGINT => 'php::BigInt::toFloat', + Type::BIGFLOAT => 'php::BigFloat::toFloat', + Type::DECIMAL => 'php::Decimal::toFloat', + default => null, + }; + if ($bigConversion !== null) { + return $bigConversion . '(' . $expr . ')'; + } if (!$this->isClosedExpr($expr, 'php::toFloat')) { return 'php::toFloat(' . $expr . ')'; } @@ -142,8 +160,17 @@ trait TypeConversionTrait return $expr; } - protected function convertBoolExpr(string $expr): string + protected function convertBoolExpr(string $expr, string $fromType = ''): string { + $bigConversion = match ($fromType) { + Type::BIGINT => 'php::BigInt::toBool', + Type::BIGFLOAT => 'php::BigFloat::toBool', + Type::DECIMAL => 'php::Decimal::toBool', + default => null, + }; + if ($bigConversion !== null) { + return $bigConversion . '(' . $expr . ')'; + } if (!$this->isClosedExpr($expr, 'php::toBool')) { return 'php::toBool(' . $expr . ')'; } diff --git a/src/Parser/UnaryExpressionTrait.php b/src/Parser/UnaryExpressionTrait.php index ab2f336d..c0ba7d93 100644 --- a/src/Parser/UnaryExpressionTrait.php +++ b/src/Parser/UnaryExpressionTrait.php @@ -34,7 +34,10 @@ trait UnaryExpressionTrait protected function parseCastInt(Expr\Cast\Int_ $node): string { $this->assertExprCanBeUsedAsValue($node->expr, 'cast operand'); - return $this->convertIntExpr($this->parseExprAsValue($node->expr)); + return $this->convertIntExpr( + $this->parseExprAsValue($node->expr), + $this->detectTypeOfExpr($node->expr) + ); } protected function parseCastString(Expr\Cast\String_ $node): string @@ -49,7 +52,10 @@ trait UnaryExpressionTrait protected function parseCastBool(Expr\Cast\Bool_ $node): string { $this->assertExprCanBeUsedAsValue($node->expr, 'cast operand'); - return $this->convertBoolExpr($this->parseExprAsValue($node->expr)); + return $this->convertBoolExpr( + $this->parseExprAsValue($node->expr), + $this->detectTypeOfExpr($node->expr) + ); } protected function parseCastObject(Expr\Cast\Object_ $node): string diff --git a/tests/compiler/bigint/bitwise_shift.phpt b/tests/compiler/bigint/bitwise_shift.phpt index d5b2697a..42284169 100644 --- a/tests/compiler/bigint/bitwise_shift.phpt +++ b/tests/compiler/bigint/bitwise_shift.phpt @@ -20,6 +20,8 @@ function main(): void { // Right shift truncates (integer division by 2^n) $c = std::bigInt("15"); echo ($c >> 1)->toString(); echo "\n"; // 7 + $negativeOdd = std::bigInt("-3"); + echo ($negativeOdd >> 1)->toString(); echo "\n"; // -2, arithmetic shift // Compound shift left $d = std::bigInt("1"); @@ -49,6 +51,7 @@ function main(): void { 64 16 7 +-2 1024 32 256 diff --git a/tests/compiler/bigint/pow-right-bigint.phpt b/tests/compiler/bigint/pow-right-bigint.phpt new file mode 100644 index 00000000..2a34c546 --- /dev/null +++ b/tests/compiler/bigint/pow-right-bigint.phpt @@ -0,0 +1,14 @@ +--TEST-- +BigInt exponentiation dispatches when BigInt is the right operand +--FILE-- +toString(), "\n"; +} +?> +--EXPECT-- +1024 diff --git a/tests/compiler/bigint/toString_cast.phpt b/tests/compiler/bigint/toString_cast.phpt index 7de4513f..baf65493 100644 --- a/tests/compiler/bigint/toString_cast.phpt +++ b/tests/compiler/bigint/toString_cast.phpt @@ -29,9 +29,9 @@ function main(): void { string(20) "12345678901234567890" string(20) "12345678901234567890" 12345678901234567890 -string(18) "3.1415926535897931" -string(18) "3.1415926535897931" -3.1415926535897931 +string(17) "3.141592653589793" +string(17) "3.141592653589793" +3.141592653589793 string(11) "0.123456789" string(11) "0.123456789" 0.123456789 diff --git a/tests/compiler/bignumber/bigfloat-high-precision.phpt b/tests/compiler/bignumber/bigfloat-high-precision.phpt new file mode 100644 index 00000000..fe318f65 --- /dev/null +++ b/tests/compiler/bignumber/bigfloat-high-precision.phpt @@ -0,0 +1,22 @@ +--TEST-- +BigFloat arithmetic retains precision beyond IEEE double +--FILE-- +toString(), "\n"; + + try { + $unused = std::bigFloat("not-a-number"); + } catch (ValueError $e) { + echo "invalid bigfloat caught\n"; + } +} +?> +--EXPECT-- +1 +invalid bigfloat caught diff --git a/tests/compiler/bignumber/bigfloat_operators.phpt b/tests/compiler/bignumber/bigfloat_operators.phpt index 8372b0ac..67a0822f 100644 --- a/tests/compiler/bignumber/bigfloat_operators.phpt +++ b/tests/compiler/bignumber/bigfloat_operators.phpt @@ -62,7 +62,7 @@ function main(): void { 400.5 70.5 502.5 -20.100000000000001 +20.1 101 42 -100.5 diff --git a/tests/compiler/bignumber/conversions-and-boundaries.phpt b/tests/compiler/bignumber/conversions-and-boundaries.phpt new file mode 100644 index 00000000..3cae92ef --- /dev/null +++ b/tests/compiler/bignumber/conversions-and-boundaries.phpt @@ -0,0 +1,65 @@ +--TEST-- +Big numeric casts, conversion functions, and runtime boundaries +--FILE-- +toString(), "\n"; +} +?> +--EXPECT-- +int(42) +float(42) +bool(true) +int(42) +float(42) +bool(false) +int(-12) +float(-12.75) +bool(true) +bool(false) +int(3) +float(3.75) +bool(true) +bool(false) +bigint range caught +invalid bigint caught +bigfloat division caught +bigfloat range caught +1E1000001 diff --git a/tests/compiler/decimal/precision-and-exception-boundaries.phpt b/tests/compiler/decimal/precision-and-exception-boundaries.phpt new file mode 100644 index 00000000..0141d5ae --- /dev/null +++ b/tests/compiler/decimal/precision-and-exception-boundaries.phpt @@ -0,0 +1,28 @@ +--TEST-- +Decimal keeps 50 digits and translates native arithmetic errors +--FILE-- +toString(), "\n"; + + try { + $unused = std::decimal("1") / 0; + } catch (DivisionByZeroError $e) { + echo "division by zero caught\n"; + } + + try { + $unused = std::decimal("not-a-decimal"); + } catch (ValueError $e) { + echo "invalid decimal caught\n"; + } +} +?> +--EXPECT-- +1234567890123456789012345678901234567890123456790 +division by zero caught +invalid decimal caught