ibelousov/math-exec

使用变量精度数字从字符串中评估表达式

2.0.0 2020-04-15 18:54 UTC

This package is auto-updated.

Last update: 2024-09-07 20:52:57 UTC


README

MathExec 是一个用于解析和评估类似这样的数学表达式的 php 库

echo Evaluator::mathExec("5 ^ 2 + 36 / 2 - 2 * 0.5");

或者您可以准备一个表达式模板,然后在需要时通过提供所需的变量以指定的精度执行它

$evaluator = Evaluator::mathPrepare("a + 5 / 3 * b - c");

foreach([[1,2,3,4],[4,5,6,4],[7,8,9,0]] as $values) {
    $evaluator->exec(['a' => $values[0], 'b' => $values[1], 'c' => $values[2]], $values[3]);
}

或者像这样

$a = 1.5E+2;
$b = 18;

return Evaluator::mathExec("$a * 4 + 30 * 2 + $b / 3");

这同样也是正确的

var_dump(Evaluator::mathExec("floor((0.7+0.1)*10)") == 8);
// boolean true

var_dump([floor((0.7+0.1)*10) == 8]);
// boolean false

它利用 Pratt 解析器,并使用 ext-bcmath 扩展来评估操作。例如这个表达式

$a = 4;
$b = 4; 
$c = 4;

Evaluator::mathExec("$a + $b / $c", 40);

在内部转换为这样

bcadd($a, bcdiv($b, $c, 40), 40);

这样自然地编写表达式非常方便,无需使用 bcadd、bcdiv 等。

安装

使用包管理器 composer 安装它

composer install ibelousov/math-exec

用法

首先导入类 Evaluator

use \Ibelousov\MathExec\Evaluator\Evaluator;

支持的事物

// Root  
Evaluator::mathExec("\\2"); 
// "1.1892071150027210667174999705604759152929"

(您可以设置内部精度,例如这样

Evaluator::mathExec("\\2", 1000); 

// "1.1892071150027210667174999705604759152929720924638174130190022
// 2471946666822691715987078134453813767371603739477476921318606372
// 6361789847756785360862538017775070151511403557092273162342868889
// 9241754460719087105038499725591050098371044920154845735674580904
// 8399409309000349779590803848965884300504119871700937907982098462
// 5235373981281740818113780828552014842210060958932412445931035057
// 5191963029413832634742802798244080228008217292720586153666393704
// 0023820730854565306744771485988873345762718678381165470458727612
// 7111269988678434930175861424970170054131455143891998743766762178
// 5161783177987307048236318734734842180537156986842636482761056228
// 4779958628963329392816878747586560347379199645940075615444371574
// 1890303986971294306248625351734129153597531121544674615908647760
// 6517445957055930979119465756398917686972170262497475333629918606
// 5311570834936807698049481706074376847467855865282550141846497924
// 8909951563378299859508764353239662147789654791045418693466186139
// 614521856391702634160435422985610854932687"

默认情况下小数点后有 40 位)

// 乘法

Evaluator::mathExec("2 * 2", 0); 
//  "4"

// 除法

Evaluator::mathExec("2 / 2", 0); 
// "1"
// Power
Evaluator::mathExec("2 ^ 3", 0); 
// "8" (left and right should be whole numbers)
// Modul
Evaluator::mathExec("7 % 2", 0); 
// "1"
// Whole division
Evaluator::mathExec("3.1415 // 2", 0); 
// "1"
// Associativity
Evaluator::mathExec("2 + 2 * 2", 0);  
// "6"
// Parenthesis
Evaluator::mathExec("(2 + 2) * 2", 0); 
// "8"
// Float to string convertion number formats
$a = 0.1415E-10;
$b = 0.1415E-10;
Evaluator::mathExec("$a * $b"); 
// "0.0000000000000000000002002225000000000000"
// comparison
Evaluator::mathExec("4 ^ 128 > 4 ^ 64"); 
// "1" 
Evaluator::mathExec("4 ^ 128 < 4 ^ 64"); 
// "0" 
Evaluator::mathExec("4 ^ 64 + 1 >= 4 ^ 64"); 
// "1"
Evaluator::mathExec("4 ^ 64 <= 4 ^ 64"); 
// "1"
Evaluator::mathExec("4 == 4"); 
// "1"
Evaluator::mathExec("4 != 4"); 
// "0"

函数

Evaluator::mathExec("floor(3.1415)"); 
// "3"
Evaluator::mathExec("ceil(3.1415)");
// "4"
Evaluator::mathExec("format(ceil(3.1415) + floor(3.1415), 2)");
// "7.00" 

您还可以添加自己的函数,例如这样

\Ibelousov\MathExec\Evaluator\BuiltinCollection::addBuiltin('inc', function($args) {
    $number_parts = explode('.', $args[0]->value);
    $precision = isset($number_parts[1]) ? strlen($number_parts[1]) : 0;

    return new \Ibelousov\MathExec\Evaluator\NumberObj(bcadd($args[0]->value, '1', $precision));
});

echo Evaluator::mathExec('inc(inc(2))',40);

请谨慎使用

数字的内部表示

例如,如果您调用这个

Evaluator::mathExec("4/4 == 6/5", 0);

它评估为 1,因为在这种情况下内部表示是 0,并且当您进行 6/5 的除法时,您得到 1 而不是 1.2

为了准确比较数字,您应该适当地设置精度。在上面的例子中,您应该这样做,以正确比较数字

Evaluator::mathExec("4/4 == 6/5", 1);

转换为原生 PHP 数字格式

(int)Evaluator::mathExec((string)PHP_INT_MAX); 
// Evaluates to PHP_INT_MAX number
(int)Evaluator::mathExec((string)PHP_INT_MIN);
// Evaluates to PHP_INT_MIN number
(float)Evaluator::mathExec('\\2 + \\2', 30);
// cuts result 2.8284271247461900976033774484193961571392 to 2.8284271247462  
(float)Evaluator::mathExec((string)PHP_FLOAT_MIN);
// Evaluates to PHP_FLOAT_MIN number
(float)Evaluator::mathExec('1.7976931348623157E+308');
// Evaluates to PHP_FLOAT_MAX. Today i have no time to figure out, why is
// (float)\Ibelousov\MathExec\Evaluator\Evaluator::mathExec((string)PHP_FLOAT_MAX)
// doesnt return correct value, but it is a fact

其他转换,例如这样

(int)Evaluator::mathExec((string)PHP_INT_MIN . ' - 1');

是不可预测的

为了以高精度和/或大数字评估和比较数字,您应该更喜欢将值存储在字符串中而不是将它们转换为 float 或 int。

REPL

您可以使用 REPL 测试表达式评估

$ ./vendor/ibelousov/math-exec/src/REPL.php
>> 4+4*4
20
Executed in 0.0025420188903809s.

贡献

欢迎拉取请求。对于重大更改,请首先打开一个问题来讨论您想要更改的内容。

请确保适当地更新测试。

许可证

MIT