PHP floating point number FAQ

Source: Internet
Author: User
This article describes in detail the floating point numbers in php and a small problem about floating point numbers in applications. if you need it, you can refer to PHP as a weak language, this feature 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 .....

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.