Understanding floating point numbers in php

Deadly 提交于 2020-01-14 10:28:08

问题


I know these questions may get asked a lot but from my reading and testing it had me confused a bit and a lot of the reading I have done has just confused me more as it is quite complex.

Some people seem to have issues with simple comparisons, however I have had no issues myself.

For example...

$num1 = 27.64;
$num2 = 27.64;

if ($num1 == $num2) {
    echo 'Good!';
} else {
    echo 'Bad!';
}

// Echo's "Good!"

...and

$num1 = 27.60;
$num2 = 27.6;

if ($num1 == $num2) {
    echo 'Good!';
} else {
    echo 'Bad!';
}

// Echo's Good

...and

$num1 = 27.60;
$num2 = 57.60;

if ($num1 <= $num2) {
    echo 'Good!';
} else {
    echo 'Bad!';
}

// Echo's Good

...and

$num1 = 25.00;
$num2 = 12.50 + 12.5;

if ($num1 == $num2) {
    echo 'Good!';
} else {
    echo 'Bad!';
}

// Echo's Good

Then I see pages like http://patchlog.com/php/comparing-float-values-in-php/ that seem to have simple issues and I don't get it.

I just want to understand how he is getting problems with his simple code but I am not with mine.


回答1:


Example 1

Those values will be the same -- you assign the same decimal literal to each variable. Compare that to this code:

$num1 = 27.64;
$num2 = 10.0 + 2.88 + 2.88 + 2.88 + 9.0; //In decimal arithmetic adds to 27.64

if ($num1 == $num2) {
    echo 'Good!';
} else {
    echo 'Bad!';
}

// Echo's "Bad!"

$num2 looks like it should be 27.64, but it really adds to something like 27.639999999999997015720509807579219341278076171875 (that's what I get when I do that calculation in Visual C++ on my machine). $num1 = 27.6400000000000005684341886080801486968994140625 (on my machine), so they differ.

Example 2

The trailing 0 makes no difference.

Example 3

The numbers are not within the floating-point "tolerance" so of course will differ.

Example 4

12.5 is exactly representable in floating point, so 12.5 + 12.5 is too (0.5 is 2^-1).




回答2:


Here is a clear example:

$a = 0;
for ($i = 0; $i < 100000; $i++) {
    $a += 0.00001;
}
print("$a\n");

You would expect you'll get 1 printed, but actually the output is 0.99999999999808.

(result is on an x86_64 architecture)




回答3:


The bigger (or smaller) the floating point number, the less precise it is. Exactly how precise will vary based on processor architecture.

Try doing all your tests at 1E30 or 1E-30...




回答4:


The first two have the value provided by the compiler, which is resolving both numbers to the same bit pattern.

I'm not going to touch the third since it should be obvious why it works.

For the fourth, the values used have well defined, fully accurate bit patterns. Try using numbers a little more off the beaten path.




回答5:


You can try

 $a = '12.30';

as string to get exact match otherwise floatbox by default remove ending '0'.




回答6:


Floating-point errors occur only when there are operations whose mathematical results cannot be exactly represented in floating point. The errors are precisely defined; they are not random or arbitrary, so identical results are produced when identical operations are performed.

In your first example, you assign “27.64” to $num1 and to $num2. There is an operation being performed here: The parser must interpret the character string “27.64” and produce a floating-point result. Likely, the parser produces the floating-point number that is closest to 27.64. (As a hexadecimal floating-point numeral, that number is 0x1.ba3d70a3d70a4p+4. The part before the “p” is a hexadecimal numeral, with a fractional part. The “p4” means multiply by 24. As a decimal numeral, it is 27.6400000000000005684341886080801486968994140625.) And it produces the same number in both cases, so the comparison of $num1 to $num2 indicates they are equal to each other, although neither is equal to 27.64 (because 27.64 cannot be exactly represented in floating point).

In your second example, the floating-point number that is closest to the value of the numeral “27.60” is the same as the floating-point number that is closest to the value of the numeral “27.6”, since the two numerals represent the same value. So, again, you get identical results.

In your third example, the values of the two numerals are far apart, so you get different floating-point numbers, and the comparison indicates they are unequal.

In your fourth example, all of the values are exactly representable in floating-point, so there is no error. 25, 12.50, and 12.5 are all small multiples of a power of two (includes powers with a negative exponent, such as .5 = 2-1, within the range of the floating-point type, so they are representable. Further, the sum of 12.50 and 12.5 is exactly representable, so there is no rounding error when adding them. Thus, all the results are exact, and the comparison indicates the sum equals 25.

Problems arise when people expect identical results from two different calculations that would have the same mathematical result. A classic example is comparing “.3” to “.1+.2”. Converting the numeral “.3” to floating-point yields the closest representable value, which is 0x1.3333333333333p-2 (0.299999999999999988897769753748434595763683319091796875), slightly under .3. Converting “.1” to floating-point yields the closest representable value, which is 0x1.999999999999ap-4 (0.1000000000000000055511151231257827021181583404541015625), slightly over .1. Converting “.2” to floating-point yields the closest representable value, which is 0x1.999999999999ap-3 (0.200000000000000011102230246251565404236316680908203125), slightly over .2. Adding the latter two values yields the representable value closest to their sum, which is 0x1.3333333333334p-2 (0.3000000000000000444089209850062616169452667236328125). As you can see, that sum is different from the value obtained by converting “.3” to floating-point, so comparing them indicates they are unequal.



来源:https://stackoverflow.com/questions/10991713/understanding-floating-point-numbers-in-php

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!