3

I know that in general two floats must be compared like abs(f1-f2) < 1e-6, but if they are rounded before comparison is it safe to compare their rounded values for equality in Python?

if round(f1,5)==round(f2,5):
    print "equal"
else
    print "unequal"

Thanks

mljrg
  • 4,430
  • 2
  • 36
  • 49

2 Answers2

4

First, it is not necessary to compare two floats with an error tolerance as you suggest. The relationship between two floating-point numbers depends on how they were derived, and that varies hugely between applications. There is no uniform answer for this.

Second, the equality-comparison of two rounded values will return true if and only if the values are equal. Allowing any sort of tolerance to get an “approximately equals” function will have no effect (if the tolerance is less than the rounding distance).

However, rounding values in floating-point is problematic because small errors may move a value from one side of a rounding point to another. For example, consider rounding f1 to the nearest integer, where f1 is some value you have computed with floating-point operations that included rounding errors. (Rounding errors occur when a mathematical result of an operation is not exactly representable, so the computer has to round it to the nearest representable result.) If f1 is very near, say, 2.5, what should it round to? Obviously, if it is less than 2.5, it will round to 2, and if it is greater, it will round to 3. But suppose the ideal value for f1, calculated with exact mathematics, is slightly greater than 2.5, but the computed value is slightly less than 2.5, due to rounding errors. Then f1 will round to 2, but we would have preferred it to round to 3.

At the same time, suppose you have an f2 that correctly rounds to 3. If you compare the rounded f1 to the rounded f2, they will be unequal.

So, after you have rounded numbers, it is too late to consider what errors might have been in them; that information is gone. Comparing numbers after they are rounded cannot tell you whether the exact mathematical results would have been close to each other before rounding.

The solution for this depends on your particular application and how the numbers are calculated. There is no general solution.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Could you point out any case where usage of exact comparison of floating point numbers would be reasonable (unless we are checking for degenerate cases like NaN or INF, of course)? – AlexErofeev Dec 29 '12 at 19:37
  • 2
    @AlexErofeev See [integers as floating point](http://stackoverflow.com/a/14061973/1798593). – Patricia Shanahan Dec 29 '12 at 19:44
  • 2
    @AlexErofeev: Clipping. You are about to calculate an arcsine, and must either use complex arithmetic or process an exception if the argument is outside [-1, 1]. Or you are working with objects in a simulation or in graphics and want to discard objects outside a boundary. Or you are converting to decimal (somebody has to write `printf`) and need to know if there are more digits or not. In these cases, it makes sense to compare exactly to a precise, fixed boundary. – Eric Postpischil Dec 29 '12 at 22:07
  • 3
    @AlexErofeev: In other cases, where an application has two numbers with some unknown errors in them, it is impossible to compare for ideal equality. That is, it is impossible to answer the question “If these had been calculated with exact mathematics, would they be equal?” It is impossible because the information is gone. You can only decide to accept different numbers as equal at the cost of not accepting different numbers as unequal. The allowable difference varies from application to application, and, in some, no difference is acceptable. There is no uniform answer. – Eric Postpischil Dec 29 '12 at 22:11
1

No. As said in documentation,

The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68.

So the answer is still floating-point number and we can't have any guarantee on its exact value.

There are examples where round() returns number, that can't be represented exactly as floating-point numbers and result still needs to use epsilon for proper comparison ( round() in Python doesn't seem to be rounding properly , for example)

Community
  • 1
  • 1
AlexErofeev
  • 161
  • 6
  • 2
    `round()` in Python works perfectly well. What do you mean when you say it isn't rounding properly? – David Heffernan Dec 29 '12 at 15:25
  • @DavidHeffernan it's not an issue of Python, it's common among any IEEE 754 - based representation of floating-point numbers. Some examples are here - http://docs.python.org/2/tutorial/floatingpoint.html#tut-fp-issues – AlexErofeev Dec 29 '12 at 15:29
  • But that's not what you said. What you mention in that comment is just the well known fact that not all real numbers are representable on a finite computer. But you actually claim that `round()` does not work properly. I suggest you edit your answer to say what you mean. – David Heffernan Dec 29 '12 at 15:31
  • @DavidHeffernan Fixed accents in the answer, hope it's ok now. – AlexErofeev Dec 29 '12 at 15:38
  • In Squeak, 2.675<(2675/1000) evaluates to true, so for me, the rounding is correct in this case. The approximation occurs before rounding when converting string '2.675' into closest base 2 IEEE 754 double – aka.nice Dec 29 '12 at 15:52
  • Does == test for bitwise equality of floats? – mljrg Dec 29 '12 at 15:55
  • @mljrg Sort of. Actually, in x86 processors equality means that result of `a-b` is 0. (Not 0.000000000001, which can happen more often than it seems) A few links: [1](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html), [2](http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=integersReals), [3](http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=integersReals2) – AlexErofeev Dec 29 '12 at 16:10
  • 2
    It is not correct that you cannot have a guarantee on the exact value of a floating-point number. IEEE 754 specifies floating-point numbers and arithmetic, and the results are reproducible when properly implemented. – Eric Postpischil Dec 29 '12 at 18:37
  • @mljrg: `==` compares floating-point values for exact equality. It returns true if and only if two floating-point values represent exactly the same number. In IEEE 754 floating point (rather than Python per se), this is not the same as bit-wise equality for two reasons. First, there are two encodings of 0: +0 and -0. They represent the same mathematical value but have different bit encodings. Second, decimal floating point allows some different representations of the same number. As an analogy, it may represent ten as 1e1 or 10e0. – Eric Postpischil Dec 29 '12 at 18:39
  • @mljrg In addition to the cases mentioned by Eric Postpischil, an IEEE 754 NaN is not equal to itself, even though it obviously has the same bit pattern as itself. The "represent exactly the same number" rule works even for this case, because a NaN does not represent any number. – Patricia Shanahan Dec 30 '12 at 18:10