Today I am learning about the first extension of mathematics. For mathematical operations, it is nothing more than all kinds of mathematical operations. Of course, mathematical operations are also one of the most basic and fundamental things in the entire program software development process. No matter what major you are studying, you will basically learn data structures and algorithms at the end. Algorithms are actually studying how to use mathematics to optimize various sorting and search capabilities. PHP has already prepared a lot of mathematical calculation functions for us at the bottom, let us learn them one by one.

What is the accuracy problem

Regarding the issue of accuracy, many small partners who have worked in finance may not be unfamiliar. Especially for front-end students, if you execute 1.1+2.2 in js, the results will often not be as you wish. This is about the storage of floating-point numbers. We all know that in the programming world, any data actually exists in binary form at the bottom. However, floating-point numbers are more complicated in storage due to the existence of the decimal point, so this type of precision loss often occurs.

But many people will be surprised that the result of directly executing 1.1+2.2 in PHP is correct. It seems that there is no such problem of loss of precision. Haha, that can only say that you are too young to simple. The problem of loss of precision is not a problem of which language, basically all languages will have this problem, but the form of expression is different.

bc precision calculation

Let's first take a look at how the loss of precision can be displayed in the PHP environment.

$a = 0.58;

echo $a * 100, PHP_EOL; // 58
echo intval($a * 100), PHP_EOL; // 57
echo (int) ($a * 100), PHP_EOL; // 57
echo intval(bcmul($a, 100)), PHP_EOL; // 58

We define a variable $a, and its content is 0.58. At this time, we multiply him directly by 100, and the result seems to be no problem. But if we force it to int type, there will be a problem. It is obviously 58. Why is it changed to 57?

In fact, after floating-point operations, the result is not 58, but a number like 57.99999999999999. If we directly echo, it will be forced through the string, this will directly output 58, but if it is forced through int, Regardless of whether it is inval() or (int), the conversion will be performed according to the rule of int forced conversion and discarding decimals. So, the result becomes 57.

Through direct echo, we often feel that there does not seem to be a loss of precision in PHP, but in fact this problem does exist. In many cases, such as storing in a database, or converting to json format, problems will be found. If you want to calculate accurately, you can use the bc extension related function, which is the bcmul() function we demonstrated last. Its function is to multiply the first parameter by the second parameter, and the result obtained is also high-precision, that is, accurate results.

Next, let's take a look at the accuracy problems in various situations of addition, subtraction, multiplication and division through the conversion of json format.

echo json_encode([
    'a1' => $a, // "a1":0.58
    'a2' => $a * 100, // "a2":57.99999999999999
    'a3' => intval($a * 100), // "a3":57
    'a4' => floatval($a * 100), // "a4":57.99999999999999
    'a5' => floatval($a), // "a5":0.58
    'a6' => intval(bcmul($a, 100)), // "a6":58

    'a7' => 1.1 + 2.2, // "a7":3.3000000000000003
    'a8' => floatval(bcadd(1.1, 2.2, 10)), // "a8":3.3

    'a9' => 2 - 1.1, // "a9":0.8999999999999999
    'a10' => floatval(bcsub(2, 1.1, 10)), // "a10":0.9

    'a11' => floatval($a * 100 / 10), // "a11":5.799999999999999
    'a12' => floatval(bcdiv($a * 100, 10, 10)), // "a12":5.8

    'a13' => 10 % 2.1, // "a13":0
    'a14' => bcmod(10, 2.1), // "a14":"1"

    'a15' => pow(1.1, 2), // "a15":1.2100000000000002
    'a16' => bcpow(1.1, 2, 30), // "a16":"1.210000000000000000000000000000"

    'a17' => sqrt(1.1), // "a17":1.0488088481701516
    'a18' => bcsqrt(1.1, 30), // "a18":"1.048808848170151546991453513679"

]), PHP_EOL;

Through this code, everyone should be able to clearly see whether the loss of precision problem in PHP exists. json_encode() will convert the data according to the type of the field when converting the data, so the accuracy problem will be more obvious. This is also a lot of students in the back-end calculations, there is no problem, but through json output to the front-end will find that the data has accuracy The cause of the problem.

a1~a6 are the contents of our first test code. It is obvious that the result of ordinary use of $a * 100 is really 57.99999999999999.

a7 and a8 are demonstrations of addition. How about it? In PHP, the result of 1.1+2.2 is actually the same as in JS. The accuracy of addition can be handled by bcadd(). In the same way, a9 and a10 are subtraction problems, and the high-precision calculation results of subtraction can be obtained through bcsub(). bcdiv() is used to handle division. Note that these functions have a third parameter, which represents the number of digits to retain the decimal point. We have all given the number of digits to retain the decimal point. The purpose is to compare it with the original calculation if there is a loss of precision.

The remainder calculation of bcmod() corresponds to the function of the% calculation symbol. Under normal circumstances, it is normal that the result of 10% 2 is 0, but here we calculate that the result of 10% 2.1 is also 0, and after using bcmod(), the result is 1, which is the correct result. bcpow() is the calculation of the power, which corresponds to the pow() function in the ordinary function. Also here we have a precision problem in the calculation of the ordinary function to the 2nd power of 1.1. Using bcpow() we display the 30-bit Decimals also did not find precision exceptions. It should be noted here that if the number of decimal places is specified in bcpow(), it will be displayed, even if the calculation result has no decimals, it will be displayed as all 0s. This is not the case with the other functions above, and will only be displayed if there are indeed decimals.

Finally, there is the bcsqrt() function, which is the root of two. There is no overflow for us to test. If you have used it and found the overflow, you can leave a message.

Comparison function

Having said the functions of various precision calculations above, let's take a look at the problem of number comparison.

echo bccomp(1, 2), PHP_EOL;   // -1
echo bccomp(1.00001, 1, 3), PHP_EOL; // 0
echo bccomp(1.00001, 1, 5), PHP_EOL; // 1

The bccomp() function is a function used to compare accuracy according to the number of decimal places. Its return result is that if the parameter 1 is less than the parameter 2, it returns -1, if it is greater than 1, it returns 1, and if it is equal to, it returns 0. The third parameter user determines which bit to compare. In this example, we can see that if only the third decimal place is compared, the results of 1.00001 and 1 are equal. And if you compare to the fifth decimal place, the difference will be reflected.

Set the decimal point and bcpowmod function

Finally, we look at two more functions.

bcscale(30);
echo bcmod(bcpow(5, 2), 2), PHP_EOL; // 1.000000000000000000000000000000
echo bcpowmod(5, 2, 2), PHP_EOL; // 1.000000000000000000000000000000

bcscale() sets the number of decimal places globally. After setting this function, all functions described above will follow the setting of bcscale() if the third decimal point function is not written.

The function of the bcpowmod() function is the same as the test code in the second line, that is, bcpow() is performed once and then bcmod() is performed again. There are not many usage scenarios for it, but the writing method is very convenient.

Summarize

In addition to the calculation functions related to bc, today's content also talked about the accuracy problem, a problem that exists in various languages. In fact, to be honest, in our daily development, it is best to store data with decimal points such as amounts in units of cents. In other words, in the background, the data saved and calculated are all integer data. When displaying on the front end, just divide by 100 and keep two decimal places. This can greatly ensure that the accuracy of the data will not be lost.

In addition, for references related to precision issues in PHP, you can take a look at the instructions on the bird's blog in the second link below. Our example 0.58 * 100 is also taken from the example in his blog.

Test code:

https://github.com/zhangyue0503/dev-blog/blob/master/php/202012/source/7. Learn arbitrary precision extension functions in PHP. php

Reference documents:

https://www.php.net/manual/zh/book.bc.php

https://www.laruence.com/2013/03/26/2884.html

Searchable on their respective media platforms [Hardcore Project Manager]


硬核项目经理
90 声望18 粉丝