Analysis of php Accuracy Calculation

  • 2021-12-12 08:08:21
  • OfStack

PHP


var_dump(intval(0.58 * 100));

The correct result is 57, not 58

Trouble caused by floating-point operation

In fact, these results are not bug of the language, but they are related to the implementation principle of the language. All digits of js are Number, including shaping, which are actually of double precision (double) type.

PHP can distinguish int from float. No matter what language, as long as it involves floating-point operations, there are similar problems, so we must pay attention when using 1.

Note: If you use +-*/ of php to calculate floating-point numbers, you may encounter some problems with wrong calculation results, such as echo intval (0.58*100) above; It will print 57 instead of 58. This is actually an bug whose binary system at the bottom of the computer can't accurately represent floating-point numbers. It is cross-language. I also encountered this problem with python. Therefore, basically most languages provide class libraries or function libraries for accurate calculation. For example, php has 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? bug of PHP?

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

Floating-point numbers, taking 64-bit length (double precision) as an example, are represented by 1-bit sign bit (E), 11-bit exponent bit (Q) and 52-bit mantissa (M) (1 for 64 bits). Sign bit: The highest bit indicates positive and negative data, 0 indicates positive number and 1 indicates negative number. Exponential bit: denotes the power of data with the base of 2, and the exponent is expressed by offset code Number: Indicates the significant number after the decimal point of data.

The key point here is that decimal is expressed in binary system. As for how decimal is expressed in binary system, you can use Baidu 1, so I won't repeat it here. The key point is to understand that 0.58 is an infinitely long value for binary system (the following number omits the implied 1).

The binary of 0.58 is basically (52 bits): 001010001111010100001010110101000010100011110.57 The binary of 0.58 is basically (52 bits):

0.58 - > 0.579999999999999960.57 - > 0.5699999999999999 As for the specific floating point multiplication of 0.58 * 100, we don't consider it so detailed. If we are interested, we can see it (Floating point), and we will look at it vaguely in mental arithmetic … 0.58 * 100 = 57.999999999

Then under your intval1, it will naturally be 57...

Thus, the key point of this question is: "You seem to have a finite decimal number, but in the binary representation of the computer, it is infinite."

So, stop thinking that this is bug of PHP, this is what it is...

PHP floating point type in progress +-*%/has inaccurate problem

Continue to look at one 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 messages for floating-point numbers:

Warning

Floating point number accuracy

Obviously, simple decimal fractions like 0.1 or 0.7 cannot be converted to an internal binary format without losing 1 point precision. This leads to confusing results: For example, floor ((0.1 +0.7) * 10) usually returns 7 instead of the expected 8, because the internal representation of the result is actually similar to 7.999999999 ….

This is related to the fact that it is impossible to accurately express certain decimal fractions in finite digits. For example, 1/3 of the decimal system becomes 0.3333333....

So never trust that the result of a floating-point number is accurate to the last 1 bit, and never compare whether two floating-point numbers are equal. If you really need higher precision, you should use any precision mathematical function or gmp function

Then we should rewrite the above formula as


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

Commonly used high-precision functions are as follows:

bcadd-Adding Two High Precision Numbers bccomp-Compares two high-precision numbers and returns-1, 0, 1 bcdiv-Dividing two high-precision digits bcmod-Finding high precision digital remainder bcmul-Multiplying two high-precision numbers bcpow-Finding High Precision Digital Power bcpowmod-Finding the power of high-precision numbers and finding the modulus, which is very common in number theory bcscale-Configure the default number of decimal points, which is equivalent to "scale=" in Linux bc bcsqrt-Finding the Square Root of High Precision Numbers bcsub-Subtracts two high precision digits

BC high-precision function library includes: addition, comparison, division, subtraction, remainder, multiplication, n power, configuration of the default number of decimal points, squared. These functions are useful when it comes to money calculation, such as the price calculation of e-commerce.


/**
*  Comparison of two high-precision numbers 
* 
* @access global
* @param float $left
* @param float $right
* @param int $scale  Exact to the number of decimal points 
* 
* @return int $left==$right  Return  0 | $left<$right  Return  -1 | $left>$right  Return  1
*/
var_dump(bccomp($left=4.45, $right=5.54, 2));
// -1
/**
*  Addition of two high-precision numbers 
* 
* @access global
* @param float $left
* @param float $right
* @param int $scale  Exact to the number of decimal points 
* 
* @return string 
*/
var_dump(bcadd($left=1.0321456, $right=0.0243456, 2));
//1.05
/**
*  Subtraction of two high-precision numbers 
* 
* @access global
* @param float $left
* @param float $right
* @param int $scale  Exact to the number of decimal points 
* 
* @return string 
*/
var_dump(bcsub($left=1.0321456, $right=3.0123456, 2));
//-1.98
/**
*  Dividing two high-precision numbers 
* 
* @access global
* @param float $left
* @param float $right
* @param int $scale  Exact to the number of decimal points 
* 
* @return string 
*/
var_dump(bcdiv($left=6, $right=5, 2));
//1.20
/**
*  Multiplication of two high-precision numbers 
* 
* @access global
* @param float $left
* @param float $right
* @param int $scale  Exact to the number of decimal points 
* 
* @return string 
*/
var_dump(bcmul($left=3.1415926, $right=2.4569874566, 2));
//7.71
/**
*  Settings bc The number of decimal points of a function 
* 
* @access global
* @param int $scale  Exact to the number of decimal points 
* 
* @return void 
*/ 
bcscale(3);
var_dump(bcdiv('105', '6.55957')); 
//php7.1 16

Related articles: