4

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.

6 Answers 6

3

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).

Sign up to request clarification or add additional context in comments.

2 Comments

Good explanation. Do you have any tips on working with floating point numbers so as to avoid issues like this?
@Brett: There is a ton of information on this subject on stackoverflow and elsewhere. One place to look regarding the rounding errors that occur during decimal to floating-point conversion is my blog, exploringbinary.com (in particular, start with exploringbinary.com/topics/… ).
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)

Comments

1

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...

Comments

1

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.

Comments

0

You can try

 $a = '12.30'; 

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

1 Comment

This will fall apart as soon as you do any math with it.
0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.