big.js和php精度函数封装

php

code

function bc()
{
    $argv = func_get_args();

    $precision = $argv[1] ?? 2;
    $formulas = $argv[0];
    bcscale(10);
    if (is_numeric($formulas)) {
        return number_format($formulas, $precision, '.', '');
    }

    $fisrt_number_regex = '((?:[-][\d\.]+|[\d\.]+))';
    $two_number_regex = '((?:[-][\d\.]+|[\d\.]+))';
    $regexs = array_map(
        fn ($operate_regex) => sprintf('/%s%s%s/', $fisrt_number_regex, $operate_regex, $two_number_regex),
        ['([\*\/\%])', '([\+\-])'] // 优先级问题
    );

    $string = str_replace(' ', '', '('.$argv[0].')');
    $string = preg_replace_callback('/\$([0-9\.]+)/', fn ($matches) => '$argv[$1]', $string);
    while (preg_match('/(()?)\(([^\)\(]*)\)/', $string, $match)) {
        while (
            preg_match($regexs[0], $match[3], $m)
            || preg_match($regexs[1], $match[3], $m)
        ) {
            switch ($m[2]) {
                case '+':
                    $result = bcadd($m[1], $m[3]);

                    break;

                case '-':
                    $result = bcsub($m[1], $m[3]);

                    break;

                case '*':
                    $result = bcmul($m[1], $m[3]);

                    break;

                case '/': // 解决不能/0的问题
                    $result = b(0) === b($m[3]) ? '0' : bcdiv($m[1], $m[3]);

                    break;

                case '%':
                    $result = bcmod($m[1], $m[3]);

                    break;
            }

            $match[3] = str_replace($m[0], $result, $match[3]);
        }
        if (!empty($match[1]) && function_exists($func = 'bc'.$match[1])) {
            $match[3] = $func($match[3]);
        }
        $string = str_replace($match[0], $match[3], $string);
    }

    return number_format((float) $string, $precision, '.', '');
}

big.js

code

import Big from 'big.js'
// import { getConfig } from '@/utils/localStorage'

const getConfig = () => ({
  price_precise: 4 // 准确度
})

export default class {
  private static instance: any
  private precis:number

  constructor (precise:number) {
    this.precis = precise
  }

  public static that () { // 当前实例
    if (!this.instance) {
      const config:any = getConfig()

      this.instance = new this(config.price_precise) // config.price_precise为精准度
    }
    return this.instance
  }

  // 当需要重新加载时 删除当前实例
  public static delInstance () {
    this.instance = undefined
  }

  public init (val:Number) { // 初始化数字
    return val.toFixed(this.precis)
  }

  public bc (formulas: string, precise = null): string { // 传递字符串 执行big计算
    formulas = `(${formulas})`
    formulas = formulas.replace(/ /g, '')
    const regex = {
      bracketRegex: /(()?)\(([^\)\(]*)\)/, // 括号的内容
      operates:[
        '([\\*\\/\\%])','([\\+\\-])'  // 运算符优先级问题
      ]
    }

    const result: any = {
      bracket: null, // 括号的内容匹配结果
      number: null, // 两数内容匹配结构
      result: '', // 两数计算的结果
      bracket_str: '' // 两数计算的公式字符串 如: 1*2
    }
    const numberRegex =(operateRegex:string) => new RegExp(`((?:[-][\\d\\.]+|[\\d\\.]+))${operateRegex}((?:[-][\\d\\.]+|[\\d\\.]+))`)

    console.log(        (numberRegex(regex.operates[0])))

    // eslint-disable-next-line no-cond-assign
    while (result.bracket = regex.bracketRegex.exec(formulas)) {
      result.bracket_str = result.bracket[3] // 括号内的字符串
      while ( // 从公式提取两数之间的计算
        // eslint-disable-next-line no-cond-assign
        result.number =
        (numberRegex(regex.operates[0]).exec(result.bracket_str) ||   numberRegex(regex.operates[1]).exec(result.bracket_str))
      ) {
        const firstNumber = result.number[1] // 第一个数
        const operator = result.number[2] // 运算符
        const secondNumber = result.number[3] // 第二个
        const big = Big(firstNumber)
        switch (operator) {
          case '+':
            result.result = big.plus(secondNumber)
            break
          case '-':
            result.result = big.minus(secondNumber)
            break
          case '*':
            result.result = big.times(secondNumber)
            break
          case '/':
            result.result = big.div(secondNumber)
            break
          case '%':
            result.result = big.mod(secondNumber)
            break
          // 异或可继续扩展
        }

        // 两数计算的结束 替换成 结果   `
        result.bracket_str = result.bracket_str.replace(result.number[0], result.result)
      }
      // 括号内部计算结束 替换为结果
      formulas = formulas.replace(result.bracket[0], result.bracket_str)
    }
    formulas = formulas.trim()
    return precise === null ? Big(formulas).toFixed(this.precis) : Big(formulas).toFixed(precise)
  }
}

test

<?php

declare(strict_types=1);

 /**
  * @internal
  * @coversNothing
  */
 final class BcTest extends TestCase
 {
     // 公式测试
     public function formulaResultProvider()
     {
         return [
             ['-2 * (2 + 2) - 4 * 2  - -1 + -3.11', bc(-18.11)],
             ['-1 - -2 - -3 - -4 -5 - -6 - -7 - -8 - -9 - -10', bc(43)],
             ['1 - -2 - -3 - -4 -5 - -6 - -7 - (-31  * 4.11) - -9 - -10', bc(164.41000000000003)],
             ['2 * (2.31 + 3.11) / 2.11 /2.11 / -2.11 + -1', bc(-2.1539365149690797)],
             ['0 * (2.31 + 3.11) / 2.11 /2.11 / -2.11 + -1', bc(-1)],
             ['0 * (2.31 + 3.11) / 2.11 /2.11 / -2.11 + -1', bc(-1)],
             ['-4.22 * -11 / -11.11 * -9.9898 / -9.211 ', bc(-4.531490652050766)],
             ['-4.22 * -11 / -11.11 * (-9.9898 - -9.211) / 6 + 9.11', bc(9.652332673267326)],
             ['-2 * 2 + 2', bc(-2)],
             ['-2 * (2 + 2)', bc(-8)],
             ['1.11 * 1.11 - 1.11  + -2 - -2 +4', bc(4.12)],
             ['1.11 * 1.11 - 1.11  + -2 - -2 +4', bc(4.12)],
             ['99.11 * (1.11 * 3 ) / (2.11 + 2) -1.11', bc(79.19080291970803)],
             ['(((1.11 * (2.11 - (3.11 * (2.11  + .4 ))) / (.91 * .2  * -.612) ) * 4  + 1.21) * 5.11 - 9999.11 % 35)', bc(1142.3420366515832)],
         ];
     }

     /**
      * @dataProvider formulaResultProvider
      *
      * @param mixed $formula
      * @param mixed $result
      */
     public function testFormula($formula, $result): void
     {
         static::assertSame($result, bc($formula));
     }
 }
└─$ ./vendor/bin/phpunit tests/BcTest.php
PHPUnit 9.5.19 #StandWithUkraine

..............                                                    14 / 14 (100%)

Time: 00:00.560, Memory: 14.00 MB

OK (14 tests, 14 assertions)

后续

修复坑 经过测试

本作品采用《CC 协议》,转载必须注明作者和本文链接
专心学习不瞎搞
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
87
粉丝
105
喜欢
480
收藏
717
排名:109
访问:8.7 万
私信
所有博文
社区赞助商