Вопрос

Since comparing floats is such a difficult and debated issue I was surprised to see that CGPointEqualToPoint and the other CG comparison methods simply do this:

CG_INLINE bool
__CGPointEqualToPoint(CGPoint point1, CGPoint point2)
{
  return point1.x == point2.x && point1.y == point2.y;
}

Isn't this going to lead to issues in some cases? Or does Objective-C perform a built-in floating point comparison with fabs, epsilon and all that? I wonder why the developers chose to use a straight equality test here.

Это было полезно?

Решение

The problem with comparing floating-point numbers is not in the comparison. It is in the numbers.

Every operation, whether it is comparison or multiplication or cosine, has to operate independently: It has some inputs, it produces some outputs. It cannot “know” that the inputs must be adjusted by some amount, unless you provide that amount as another input.

When you compare floating-point numbers, or when you perform any operation on them, consider what happens if the numbers are the result of prior calculations which introduced rounding errors. You intended to compute some mathematical values x and y, but instead you have some approximations x and y.

Now consider this question: If you want to calculate x+y, but you do not know x or y because you only have the numbers x and y, which are likely different, how can you find x+y? You cannot; it is impossible. Of course, you can usually make an approximation, if you know that x and y are approximately x and y.

Now apply this to comparison. If you want to determine whether x equals y, but you do not know x or y because you only have the numbers x and y, which are likely different, how can you find whether x equals y? You cannot; it is impossible.

In the case of comparison, though, you cannot make an approximation. This is because the derivative of comparison is effectively infinite: A tiny change can cause a complete change in the value (from false to true or vice-versa).

This can only be fixed by designing each application specifically to avoid or deal with the problem in its own way. For some applications, accepting two numbers as equal when their approximations are close to each other is okay. For other applications, that will break the application. In some situations, the amount by which the numbers can be allowed to differ might be proportional to one of the numbers. In other situations, it might be proportional to some input value or other intermediate value of the calculation. Or an absolute tolerance might be suitable. Or it might be extremely difficult to calculate the amount by which the numbers can be allowed to differ.

There is no general solution, so there is no general routine for “approximately” comparing floating-point operations. Instead, elementary operations are provided to an application: Comparison for equality is, exactly, comparison for equality. Going beyond that is something you must design specifically for your application.

Consider calculating sqrt(x) or acos(y). What happens if floating-point errors have made x less than zero when the true mathematical x would not be negative, or if y is greater than one when the true mathematical y would be less than one? In these cases, sqrt and acos generate NaNs or traps. Would you want the math library to replace them with “approximate” square-root or arc-cosine functions that provided fudged answers in these cases? That is not workable. It must be recognized that the problem exists before the function is called, and it must be dealt with by the application designer.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top