Home  >  Article  >  Backend Development  >  PHP achieves high-precision calculation

PHP achieves high-precision calculation

小云云
小云云Original
2018-03-27 14:58:474617browse

This article mainly shares with you how to achieve high-precision calculations in PHP. I work in the financial industry and frequently calculate funds. Here I will talk about the pitfalls I encountered... If you are not careful, users may lose hundreds of thousands of funds, or even more terrifying. ..... Let’s go directly to the example:

javascript

0.1 + 0.2 Why is it not equal to 0.3? (Correct result: 0.30000000000000004)

0.8 * 7 Why not Equal to 5.6? (Correct result: 5.6000000000000005)

PHP

var_dump(intval(0.58 * 100));

The correct result is 57, not 58

The trouble caused by floating point operations

In fact, these results are not bugs in the language, but are related to the implementation principle of the language. All numbers in js are unified as Number, including integers. In fact, they are all double precision (double) types. .

And PHP will distinguish between int and float. No matter what language, as long as floating point operations are involved, there are similar problems, so you must pay attention when using them.

Note: If you use PHP's +-*/ to calculate floating point numbers, you may encounter some problems with incorrect calculation results. For example, the above echo intval(0.58*100); will print 57 instead of 58. This is actually a bug in the computer's underlying binary that cannot accurately represent floating point numbers. It is cross-language. I also encountered this problem using python. So basically most languages ​​provide class libraries or function libraries for precise calculations. For example, PHP has a BC high-precision function library. Later I will introduce some commonly used BC high-precision functions.

Let’s go back to questions 57 and 58 above.

Why is the output 57? Is it a PHP bug?

To understand this reason, we first need to know the representation of floating point numbers (IEEE 754):

Floating Points, taking a 64-bit length (double precision) as an example, will be represented by 1 sign bit (E), 11 exponent bits (Q), and 52 mantissa bits (M) (a total of 64 bits).

Sign bit: The highest bit represents the sign of the data, 0 represents a positive number, and 1 represents a negative number.

Exponent bit: represents the power of the data with base 2, and the exponent is represented by an offset code

Mantissa: represents the valid digits after the decimal point of the data.

The key points here It lies in the representation of decimals in binary. Regarding how to represent decimals in binary, you can search on Baidu. I will not go into details here. The key thing we need to understand is that for binary representation, 0.58 is an infinitely long value (the numbers below Omitting the implicit 1)..

The binary representation of 0.58 is basically (52 bits): 00101000111101011100001010001111010111000010100011110.57 is basically (52 bits): 00100011110101110 0001010001111010111000010100011110 and binary of both, if just pass If these 52 bits are calculated, they are: www.111cn.net

0.58 -> 0.579999999999999960.57 -> 0.5699999999999999 As for the specific floating point multiplication of 0.58 * 100, we do not consider that details, if you are interested You can see (Floating point), let's look at it vaguely with mental arithmetic... 0.58 * 100 = 57.999999999

Then if you intval it, it will naturally be 57...

It can be seen that the key to this problem The point is: "Your seemingly finite decimal is actually infinite in the binary representation of the computer"

Therefore, don't think this is a PHP bug anymore, this is what it is...

PHP floating point type has inaccuracy in +-*%/

Continue to look at a piece of code:

$a = 0.1;
$b = 0.7;
var_dump(($a + $b) == 0.8); // false

The printed value is boolean false

Why is this? The PHP manual has the following warning message for floating point numbers:

Warning

Floating point precision

  显然简单的十进制分数如同 0.1 或 0.7 不能在不丢失一点点精度的情况下转换为内部二进制的格式。这就会造成混乱的结果:例如,floor((0.1+0.7)*10) 通常会返回 7 而不是预期中的 8,因为该结果内部的表示其实是类似 7.9999999999…。

  这和一个事实有关,那就是不可能精确的用有限位数表达某些十进制分数。例如,十进制的 1/3 变成了 0.3333333. . .。

 所以永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。如果确实需要更高的精度,应该使用任意精度数学函数或者 gmp 函数

那么上面的算式我们应该改写为

$a = 0.1;
$b = 0.7;
var_dump(bcadd($a,$b,2) == 0.8); // true

常用的高精度函数如下:

bc是Binary Calculator的缩写

bcadd — 将两个高精度数字相加
  bccomp — 比较两个高精度数字,返回-1, 0, 1
  bcp — 将两个高精度数字相除
  bcmod — 求高精度数字余数
  bcmul — 将两个高精度数字相乘
  bcpow — 求高精度数字乘方
  bcpowmod — 求高精度数字乘方求模,数论里非常常用
  bcscale — 配置默认小数点位数,相当于就是Linux bc中的”scale=”
  bcsqrt — 求高精度数字平方根
  bcsub — 将两个高精度数字相减
BC高精确度函数库包含了:相加,比较,相除,相减,求余,相乘,n次方,配置默认小数点数目,求平方。这些函数在涉及到有关金钱计算时比较有用,比如电商的价格计算。
/**
  * 两个高精度数比较
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return int $left==$right 返回 0 | $left<$right 返回 -1 | $left>$right 返回 1
  */
var_dump(bccomp($left=4.45, $right=5.54, 2));
// -1
  
 /**
  * 两个高精度数相加
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return string 
  */
var_dump(bcadd($left=1.0321456, $right=0.0243456, 2));
//1.05
 
  /**
  * 两个高精度数相减
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return string 
  */
var_dump(bcsub($left=1.0321456, $right=3.0123456, 2));
//-1.98
  
 /**
  * 两个高精度数相除
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return string 
  */
var_dump(bcp($left=6, $right=5, 2));
//1.20
 
 /**
  * 两个高精度数相乘
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return string 
  */
var_dump(bcmul($left=3.1415926, $right=2.4569874566, 2));
//7.71
 
 /**
  * 设置bc函数的小数点位数
  * 
  * @access global
  * @param int $scale 精确到的小数点位数
  * 
  * @return void 
  */ 
bcscale(3);
var_dump(bcp(&#39;105&#39;, &#39;6.55957&#39;)); 
//php7.1 16

所以平时程序要要封装方法:

/**
 * 精确加法
 * @param [type] $a [description]
 * @param [type] $b [description]
 */
function math_add($a,$b,$scale = &#39;2&#39;) {
  return bcadd($a,$b,$scale);
}
/**
 * 精确减法
 * @param [type] $a [description]
 * @param [type] $b [description]
 */
function math_sub($a,$b,$scale = &#39;2&#39;) {
  return bcsub($a,$b,$scale);
}
/**
 * 精确乘法
 * @param [type] $a [description]
 * @param [type] $b [description]
 */
function math_mul($a,$b,$scale = &#39;2&#39;) {
  return bcmul($a,$b,$scale);
}
/**
 * 精确除法
 * @param [type] $a [description]
 * @param [type] $b [description]
 */
function math_p($a,$b,$scale = &#39;2&#39;) {
  return bcp($a,$b,$scale);
}
/**
 * 精确求余/取模
 * @param [type] $a [description]
 * @param [type] $b [description]
 */
function math_mod($a,$b) {
  return bcmod($a,$b);
}
/**
 * 比较大小
 * @param [type] $a [description]
 * @param [type] $b [description]
 * 大于 返回 1 等于返回 0 小于返回 -1
 */
function math_comp($a,$b,$scale = &#39;5&#39;) {
  return bccomp($a,$b,$scale); // 比较到小数点位数
}

相关推荐:

php如何处理高精度计算函数

The above is the detailed content of PHP achieves high-precision calculation. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn