A common problem with PHP floating point numbers and a common problem with PHP floating point numbers. PHP floating point number is a common problem. PHP floating point is a weak language. such a feature requires seamless and transparent implicit type conversion, A common problem of using zval in PHP to protect PHP floating point numbers.
PHP is a weak type language, which requires seamless and transparent implicit type conversion. PHP uses zval to store any type of values, the structure of zval is as follows (5.2 is used as an example ):
struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount; zend_uchar type; /* active type */ zend_uchar is_ref;};
In the above structure, the zvalue_value consortium is actually saved for the value itself:
typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { char *val; int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj;} zvalue_value;
In today's topic, we only focus on two of them, lval and dval. we need to realize that long lval varies with the compiler and OS length, it may be 32bits or 64 bits, while double dval (dual precision) is defined by IEEE 754, which is fixed length and must be 64bits.
Keep this in mind, and make some PHP code "non-platform-independent". In our next discussion, except for some specific instructions, we assume that long is 64 bits.
The floating point counting method of IEEE 754 is not referenced here. if you are interested, you can check it by yourself. The key point is that the ending number of double is saved in 52 bits, the hidden 1-bit valid bit. The total value is 53bits.
Here, a very interesting question is raised. We use the c code example (assuming that long is 64 bits ):
long a = x; assert(a == (long)(double)a);
When the value of a is within the scope, can the above code assert success? (Stay at the end of the article)
Now let's get back to the question. before executing a script, PHP needs to read the script and analyze the script. this process also includes zval for the literal volume in the script, for example, for the following script:
<? Php $ a = 9223372036854775807; // maximum number of 64-bit signed characters $ B = 9223372036854775808; // maximum value + 1var_dump ($ a); var_dump ($ B );
Output:
int(9223372036854775807)float(9.22337203685E+18)
That is to say, in the lexical analysis phase of PHP, a literal value is determined to determine whether it exceeds the long table value range of the current system. if not, lval is used for saving. zval is IS_LONG. Otherwise, it is represented by dval, and zval IS_FLOAT.
Take care of any value greater than the largest integer, because it may cause loss of precision:
<?php$a = 9223372036854775807;$b = 9223372036854775808; var_dump($a === ($b - 1));
The output is false.
Now let's start with the discussion. as we have said before, the PHP integer may be 32-bit or 64-bit, which determines that some code can run normally on 64-bit, this may cause loss of precision due to invisible type conversion, resulting in code failure to run on a 32-bit system.
Therefore, we must be aware of this critical value. Fortunately, this critical value has been defined in PHP:
<?php echo PHP_INT_MAX; ?>
Of course, to be safe, we should use strings to store large integers and use mathematical libraries such as bcmath for calculation.
In addition, there is another key configuration that will confuse us. This configuration is php. precision, which determines how many valid bits are output when PHP outputs another float value.
Finally, let's look back at the above question, that is, a long integer. what is the maximum value to ensure that precision will not be lost if it is converted back to float?
For example, for an integer, we know that its binary representation is 101. now, let's shift the two digits right to 1.01, and remove the hidden valid digit 1 in the high position, the binary value of 5 stored in double is as follows:
0/* symbol bit */10000000001/* Index bit */0100000000000000000000000000000000000000000000000000
The binary representation of 5 is stored in the tail part without any loss. in this case, the transfer from double to long will not cause loss of precision.
We know that double represents the ending number with 52 bits, and the hidden first 1 is regarded as the total precision of 53 bits .. then we can conclude that if the value of a long integer is smaller:
2 ^ 53-1 = 9007199254740991; // keep in mind that we now assume that it is a long
Then, this integer will not be lost in precision when the value of long-> double-> long is converted.
Another point about floating point numbers is to answer the following Frequently Asked Questions:
<? Php $ f = 0.58; var_dump (intval ($ f * 100); // why output 57?>
Why is output 57? PHP bug?
I believe many people have such questions, because there are many people who ask similar questions, not to mention bugs.php.net...
To understand this, first we need to know the floating point representation (IEEE 754 ):
Floating point number. taking the 64-bit length (double precision) as an example, one-bit sign bit (E), 11 index bit (Q), and 52-bit ending number (M) are used) (64-bit in total ).
Symbol bit: the highest bit indicates positive and negative data, 0 indicates positive data, and 1 indicates negative data.
Exponent bit: the power of data at the bottom of 2, and the exponent is represented by an offset code.
Ending number: a valid number after the decimal point of the data.
The key point here is that decimal places are represented in binary. you can refer to Baidu for how decimal places are represented in binary. I will not describe them here. we need to understand the key points, 0.58 for binary representation, it is an infinitely long value (the following number saves the implicit 1 )..
The binary representation of 0.58 is basically (52 bits): 0010100011110101110000101000111101011100001010001111
The binary representation of 0.57 is basically (52 bits): 0010001111010111000010100011110101110000101000111101
The binary values of the two are:
0.58-> 0.57999999999999996
0.57-> 0.56999999999999995
As for the specific Floating-point multiplication of 0.58*100, we will not consider that detail. if you are interested, you can see the Floating point. we will look at it in a fuzzy way... 0.58*100 = 57.999999999
Then you intval It. Naturally, it's 57 ....
It can be seen that the key point of this problem is: "You seem to have a poor decimal number, but it is infinite in the binary representation of the computer"
So, no longer think this is a PHP bug. this is the case .....
Articles you may be interested in:
- Introduction to integer and floating point numbers of PHP Data types
- Use of the sprintf function in php to control the floating point format
- How to compare and integer inaccuracy of floating point numbers in PHP
- Php's method of determining whether two floating point numbers are equal
- PHP floating point number accuracy problem summary
- You should know about PHP floating point
- Analysis of Two float (floating point number) comparison instances in PHP
- A brief introduction to php floating point exact operation
Pluphp is a weak type language. such a feature requires seamless and transparent implicit type conversion. PHP uses zval internally to protect the file...