为什么要使用 bcmath
首先我们要明白为什么要使用 bcmath
,因为我们平时没怎么使用它们,但是我们在做高精度计算,或者金钱计算的时候对结果要尽可能的精确且可控,但是编程语言对浮点数和大整数的运算,表示,存储方面都存在一定的问题,这个和语言无关。
先来看浮点数的问题
$f = 0.58;
$r = $f * 100;
var_dump($r); // float(58)
var_dump(intval($r)); // int(57)
首先就是你看到的和实际内存中存储的不一致,实际存储可能是 0.58 -> 0.57999999999999996
,而 $r -> 57.999999999999996
,这就出现了不可控,甚至到了不可用的地步。
其次是大的整数,而整数能存储的最大值是受操作系统限制的,比如有32位和64位系统之分,当你要存储的值超过了上限,那么就会被当做浮点数来存了,牵涉到浮点数就可能存在计算偏差。
$a = PHP_INT_MAX;
$b = PHP_INT_MAX+1;
var_dump($a); // int(2147483647)
var_dump($b); // float(2147483648)
var_dump($a === $b - 1); // bool(false)
针对以上的问题,可以使用字符串来存储浮点数和大整数。
$f = 0.58;
$r = strval($f*100);
var_dump(intval($r)); // int(58)
基于这样的思路,于是有了 PHP bcmath
,每个语言都有相对应的实现。
关于 bcmath (Binary Calculator)
对于任意精度的数学,PHP提供了支持用字符串表示的任意大小和精度的数字的二进制计算,最多为 2147483647-1(或0x7FFFFFFF-1)
。
本类函数仅在 PHP 编译时配置了 --enable-bcmath
时可用。PHP 的 Windows 版本已内建对此扩展的支持。不需要载入额外的扩展来使用这些函数。
它包含了如下的一些计算:相加,比较,相除,相减,求余,相乘,n次方,配置默认小数点数目,求平方等等。这些函数在涉及到有关金钱的计算时比较有用。
bcadd
相加bccomp
比较两个任意精度的数字,结果 1,0,-1bcdiv
2个任意精度的数字除法计算bcmod
对一个任意精度数字取模bcmul
2个任意精度数字乘法计算bcpow
任意精度数字的乘方bcpowmod
先乘方,再取模bcscale
设置所有bc数学函数的默认小数点保留位数bcsqrt
任意精度数字的二次方根bcsub
2个任意精度数字的减法
bcadd
2个任意精度数字的加法计算
bcadd ( string $left_operand , string $right_operand , int $scale = ? ) : string
int $scale
此可选参数用于设置结果中小数点后的小数位数。也可通过使用 bcscale() 来设置全局默认的小数位数,用于所有函数。如果未设置,则默认为 0
。
$a = '1.234';
$b = '5.366';
var_dump(bcadd($a, $b)); // string(1) "6"
var_dump(bcadd($a, $b, 4)); // string(6) "6.6000"
$a = '1.2348111111111111111111';
$b = '5.3152222222222222222222';
var_dump(bcadd($a, $b)); // string(1) "6"
var_dump(bcadd($a, $b, 2)); // string(6) "6.55"
小数的位数是相加后四舍五入再来截取的,位数不够的补0
bccomp
比较两个任意精度的数字的大小
bccomp ( string $left_operand , string $right_operand , int $scale = int ) : int
可选的scale
参数表示要以几位小数来比较。
如果两个数相等返回0, 左边的数left_operand
比较右边的数right_operand
大返回1, 否则返回-1
var_dump(bccomp('1', '2')); // int(-1)
var_dump(bccomp('1.00001', '-1', 3)); // int(1)
var_dump(bccomp('1.00001', '1', 3)); // int(0)
var_dump(bccomp('1.00001', '1', 5)); // int(1)
bcdiv
2个任意精度的数字除法计算
bcdiv ( string $left_operand , string $right_operand , int $scale = int ) : string
scale
此可选参数用于设置结果中小数点后的小数位数。也可通过使用 bcscale() 来设置全局默认的小数位数,用于所有函数。如果未设置,则默认为 0
。
var_dump(bcdiv('105', '6.55957', 3)); // 16.007
bcmod
对一个任意精度数字取模
bcmod ( string $left_operand , string $modulus ) : string
返回字符串类型取模后结果,如果系数为0则返回null
echo bcmod('4', '2'); // 0
echo bcmod('2', '4'); // 2
bcmul
2个任意精度数字乘法计算
bcmul ( string $left_operand , string $right_operand , int $scale = int ) : string
echo bcmul('1.34747474747', '35', 3); // 47.161
echo bcmul('2', '4'); // 8
bcpow
任意精度数字的乘方
bcpow ( string $left_operand , string $right_operand , int $scale = ? ) : string
echo bcpow('4.2', '3', 2); // 74.08
bcpowmod
先乘方,再取模
bcpowmod ( string $num , string $exponent , string $modulus , int|null $scale = null ) : string
var_dump(bcpowmod('3', '3', '2', 0)); // string(1) "1"
bcscale
设置所有bc数学函数的默认小数点保留位数
bcscale ( int $scale ) : bool
bcsqrt
任意精度数字的二次方根
bcsqrt ( string $operand , int $scale = ? ) : string
bcsub
2个任意精度数字的减法
bcsub ( string $left_operand , string $right_operand , int $scale = int ) : string
参考
https://www.laruence.com/2013/03/26/2884.html
https://www.laruence.com/2011/12/19/2399.html