php 高精度计算的问题

201 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情
最近在项目碰到比较小的金额计算问题,然后就碰上了9.95+0.01的这样的数值,发现这个在js和php中直接用+号计算结果都是9.959999999999999,而不是9.96

1.  $a=9.95;
    
2.  $b= 0.01;
    
3.  $count=$a+$b; //这里输出是9.959999999999999
    
4.  //正确应该是9.96才对

查了一下手册发现

对于任意精度的数学, 如果有足够多的内存,PHP 提供的 BCMath 支持用字符串的形式表示任意大小和精度的数字,最大尺寸为 2147483647(即 0x7FFFFFFF)。

有效(即格式良好)的 BCMath 数字是匹配正则表达式 /^[+-]?[0-9]*(.[0-9]*)?$/ 的字符串。

php 是有高精度计算函数

bcadd — 2个任意精度数字的加法计算
bccomp — 比较两个任意精度的数字
bcdiv — 2个任意精度的数字除法计算
bcmod — 对一个任意精度数字取模
bcmul — 2个任意精度数字乘法计算
bcpow — 任意精度数字的乘方
bcpowmod — Raise an arbitrary precision number to another, reduced by a specified modulus
bcscale — 设置所有bc数学函数的默认小数点保留位数
bcsqrt — 任意精度数字的二次方根
bcsub — 2个任意精度数字的减法
2.
//mm和n代表传入的两个数值,主要就是这两个数值之间的比较
//x代表传入的方法,比如是;add,sub//x代表传入的方法,比如是;add,sub等 //scale 代表传入的小数点位数。这个根据需求更改即可

public function calc($m,$n,$x,$scale){
    $errors=array(
      '被除数不能为零',
      '负数没有平方根'
    );
    switch($x){
      case 'add':
        $t=bcadd($m,$n,$scale);
        break;
      case 'sub':
        $t=bcsub($m,$n,$scale);
        break;
      case 'mul':
        $t=bcmul($m,$n);
        break;
      case 'div':
        if($n!=0){
          $t=bcdiv($m,$n);
        }else{
          return $errors[0];
        }
        break;
      case 'pow':
        $t=bcpow($m,$n);
        break;
      case 'mod':
        if($n!=0){
          $t=bcmod($m,$n);
        }else{
          return $errors[0];
        }
        break;
      case 'sqrt':
        if($m>=0){
          $t=bcsqrt($m);
        }else{
          return $errors[1];
        }
        break;
    }
      return $t;
  }

调用方式:

 $result= $this->calc(2.001,3.002,'sub',3);