|
@@ -791,9 +791,10 @@ class Service
|
|
|
/**
|
|
|
* 用 bcmath 安全执行表达式 (支持 + - * /)
|
|
|
*/
|
|
|
- public function evaluateWithBCMath($expr, $decimals = 2)
|
|
|
+ public function evaluateWithBCMath1($expr, $decimals = 2)
|
|
|
{
|
|
|
- preg_match_all('/(\d+(?:\.\d+)?|[+\/*-])/', $expr, $matches);
|
|
|
+// preg_match_all('/(\d+(?:\.\d+)?|[+\/*-])/', $expr, $matches);
|
|
|
+ preg_match_all('/(-?\d+(?:\.\d+)?|[+\/*-])/', $expr, $matches);
|
|
|
$tokens = $matches[0] ?? [];
|
|
|
|
|
|
if (empty($tokens)) return 0;
|
|
@@ -835,4 +836,67 @@ class Service
|
|
|
|
|
|
return $result;
|
|
|
}
|
|
|
+
|
|
|
+ public function evaluateWithBCMath($expr, $decimals = 2)
|
|
|
+ {
|
|
|
+ $calcDecimals = 8; // 中间计算用更高精度
|
|
|
+ // 支持负数和小数
|
|
|
+ preg_match_all('/-?\d+(?:\.\d+)?|[+\/*-]/', $expr, $matches);
|
|
|
+ $tokens = $matches[0] ?? [];
|
|
|
+
|
|
|
+ if (empty($tokens)) return '0';
|
|
|
+
|
|
|
+ // Shunting-yard 算法转换为逆波兰表达式 (RPN)
|
|
|
+ $output = [];
|
|
|
+ $ops = [];
|
|
|
+ $precedence = ['+' => 1, '-' => 1, '*' => 2, '/' => 2];
|
|
|
+
|
|
|
+ foreach ($tokens as $token) {
|
|
|
+ if (is_numeric($token)) {
|
|
|
+ $output[] = (string)$token;
|
|
|
+ } elseif (isset($precedence[$token])) {
|
|
|
+ while (!empty($ops) && $precedence[end($ops)] >= $precedence[$token]) {
|
|
|
+ $output[] = array_pop($ops);
|
|
|
+ }
|
|
|
+ $ops[] = $token;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ while (!empty($ops)) {
|
|
|
+ $output[] = array_pop($ops);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算 RPN
|
|
|
+ $stack = [];
|
|
|
+ foreach ($output as $token) {
|
|
|
+ if (is_numeric($token)) {
|
|
|
+ $stack[] = (string)$token;
|
|
|
+ } else {
|
|
|
+ $b = array_pop($stack) ?? '0';
|
|
|
+ $a = array_pop($stack) ?? '0';
|
|
|
+
|
|
|
+ switch ($token) {
|
|
|
+ case '+':
|
|
|
+ $stack[] = bcadd($a, $b, $calcDecimals);
|
|
|
+ break;
|
|
|
+ case '-':
|
|
|
+ $stack[] = bcsub($a, $b, $calcDecimals);
|
|
|
+ break;
|
|
|
+ case '*':
|
|
|
+ $stack[] = bcmul($a, $b, $calcDecimals);
|
|
|
+ break;
|
|
|
+ case '/':
|
|
|
+ if (bccomp($b, '0', $calcDecimals) === 0) {
|
|
|
+ $stack[] = '0';
|
|
|
+ } else {
|
|
|
+ $stack[] = bcdiv($a, $b, $calcDecimals);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $result = array_pop($stack) ?? '0';
|
|
|
+ // 最终统一格式化为指定小数位
|
|
|
+ return bcadd($result, '0', $decimals);
|
|
|
+ }
|
|
|
}
|