我知道UIKit使用CGFloat,因为它是分辨率无关的坐标系。

但每次我想检查frame.origin.x是否为0时,我都觉得很恶心:

if (theView.frame.origin.x == 0) {
    // do important operation
}

当与==,<=,>=,<,>比较时,CGFloat是否容易出现假阳性? 它是一个浮点数,它们有不精确的问题:例如0.0000000000041。

Objective-C在比较时是否会在内部处理这个或者是否会发生原点。读作0的X不与0比较为真?


当前回答

-(BOOL)isFloatEqual:(CGFloat)firstValue secondValue:(CGFloat)secondValue{

BOOL isEqual = NO;

NSNumber *firstValueNumber = [NSNumber numberWithDouble:firstValue];
NSNumber *secondValueNumber = [NSNumber numberWithDouble:secondValue];

isEqual = [firstValueNumber isEqualToNumber:secondValueNumber];

return isEqual;

}

其他回答

与0比较是安全的操作,只要0不是一个计算值(如上面的回答所述)。这样做的原因是0在浮点数中是一个完全可表示的数字。

谈到完全可表示的值,您可以在2的幂概念中获得24位范围(单精度)。所以12 4是完全可表示的,。5。25和。125也是。只要你所有重要的比特都是24比特的,你就是黄金。所以10.625可以被精确地表示出来。

这很好,但在压力下很快就会崩溃。我脑海中浮现出两种场景: 1)当涉及到计算时。不要相信√(3)*√(3)== 3。只是不会是那样的。它可能不会在一个范围内,就像其他答案暗示的那样。 2)当涉及任何非2的幂(NPOT)时。所以这听起来可能很奇怪,但是0.1是二进制的无限级数,因此任何涉及这样一个数字的计算从一开始就不精确。

(哦,原来的问题提到了与零的比较。不要忘记-0.0也是一个完全有效的浮点值。)

因为0完全可以表示为一个IEEE754浮点数(或者使用我曾经使用过的任何其他f-p数字实现),与0比较可能是安全的。然而,如果你的程序计算了一个值(比如theView.frame.origin.x),你有理由相信它应该是0,但你的计算不能保证它是0,你可能会被咬。

为了澄清一点,计算如下:

areal = 0.0

会(除非你的语言或系统坏了)创建一个值(areal==0.0)返回true,但另一个计算,如

areal = 1.386 - 2.1*(0.66)

可能不会。

如果你能保证你的计算产生的值是0(而不仅仅是它们产生的值应该是0),那么你可以继续将f-p值与0进行比较。如果你不能保证自己达到要求的程度,最好坚持通常的“宽容平等”方法。

在最坏的情况下,不小心比较f-p值可能是极其危险的:想想航空电子设备、武器制导、发电厂操作、车辆导航,几乎所有计算与现实世界相结合的应用。

对《愤怒的小鸟》来说,没有那么危险。

[“正确答案”掩盖了选择K。选择K就像选择VISIBLE_SHIFT一样特别,但选择K不那么明显,因为与VISIBLE_SHIFT不同,它不基于任何显示属性。因此选择你的毒药-选择K或选择VISIBLE_SHIFT。这个答案主张选择VISIBLE_SHIFT,然后演示了选择K的困难。

正是由于四舍五入的错误,您不应该在逻辑操作中使用“精确”值的比较。在视觉显示的特定情况下,位置是0.0还是0.0000000003可能无关紧要——肉眼是看不见差异的。所以你的逻辑应该是这样的:

#define VISIBLE_SHIFT    0.0001        // for example
if (fabs(theView.frame.origin.x) < VISIBLE_SHIFT) { /* ... */ }

然而,最终,“看不见的眼睛”将取决于你的显示属性。如果你能上界显示(你应该可以);然后选择VISIBLE_SHIFT作为上限的一个分数。

现在,“正确答案”取决于K,所以让我们来探索选择K。

K是一个常数,你选择使你的累积误差 计算的最后一个位置肯定是K个单位(和 如果你不确定误差范围的计算是正确的,取K a 比你的计算结果大几倍)

所以我们需要K,如果得到K比选择VISIBLE_SHIFT更难,更不直观,那么你就决定什么对你有效。为了找到K,我们要写一个测试程序,看一堆K的值,这样我们就能看到它的行为。如果“正确答案”可用,那么如何选择K应该是显而易见的。没有?

我们将使用,作为“正确答案”的细节:

if (fabs(x-y) < K * DBL_EPSILON * fabs(x+y) || fabs(x-y) < DBL_MIN)

我们试一下K的所有值:

#include <math.h>
#include <float.h>
#include <stdio.h>

void main (void)
{
  double x = 1e-13;
  double y = 0.0;

  double K = 1e22;
  int i = 0;

  for (; i < 32; i++, K = K/10.0)
    {
      printf ("K:%40.16lf -> ", K);

      if (fabs(x-y) < K * DBL_EPSILON * fabs(x+y) || fabs(x-y) < DBL_MIN)
        printf ("YES\n");
      else
        printf ("NO\n");
    }
}
ebg@ebg$ gcc -o test test.c
ebg@ebg$ ./test
K:10000000000000000000000.0000000000000000 -> YES
K: 1000000000000000000000.0000000000000000 -> YES
K:  100000000000000000000.0000000000000000 -> YES
K:   10000000000000000000.0000000000000000 -> YES
K:    1000000000000000000.0000000000000000 -> YES
K:     100000000000000000.0000000000000000 -> YES
K:      10000000000000000.0000000000000000 -> YES
K:       1000000000000000.0000000000000000 -> NO
K:        100000000000000.0000000000000000 -> NO
K:         10000000000000.0000000000000000 -> NO
K:          1000000000000.0000000000000000 -> NO
K:           100000000000.0000000000000000 -> NO
K:            10000000000.0000000000000000 -> NO
K:             1000000000.0000000000000000 -> NO
K:              100000000.0000000000000000 -> NO
K:               10000000.0000000000000000 -> NO
K:                1000000.0000000000000000 -> NO
K:                 100000.0000000000000000 -> NO
K:                  10000.0000000000000000 -> NO
K:                   1000.0000000000000000 -> NO
K:                    100.0000000000000000 -> NO
K:                     10.0000000000000000 -> NO
K:                      1.0000000000000000 -> NO
K:                      0.1000000000000000 -> NO
K:                      0.0100000000000000 -> NO
K:                      0.0010000000000000 -> NO
K:                      0.0001000000000000 -> NO
K:                      0.0000100000000000 -> NO
K:                      0.0000010000000000 -> NO
K:                      0.0000001000000000 -> NO
K:                      0.0000000100000000 -> NO
K:                      0.0000000010000000 -> NO

啊,所以K应该是1e16或者更大如果我想让1e13等于0。

所以,我认为你有两个选择:

就像我建议的那样,用你的工程判断来做一个简单的计算。如果你做的是图形,“零”意味着“可见的变化”,那就检查你的视觉资产(图像等)并判断epsilon可以是什么。 不要尝试任何浮点计算,直到你阅读了非货物崇拜答案的参考资料(并在此过程中获得博士学位),然后使用你的非直觉判断选择K。

First of all, floating point values are not "random" in their behavior. Exact comparison can and does make sense in plenty of real-world usages. But if you're going to use floating point you need to be aware of how it works. Erring on the side of assuming floating point works like real numbers will get you code that quickly breaks. Erring on the side of assuming floating point results have large random fuzz associated with them (like most of the answers here suggest) will get you code that appears to work at first but ends up having large-magnitude errors and broken corner cases.

首先,如果你想用浮点数编程,你应该读一下:

每个计算机科学家都应该知道浮点运算

是的,通读一遍。如果这对你来说负担太大,你应该使用整数/固定点来计算,直到你有时间阅读它。: -)

现在,说了这么多,精确浮点比较的最大问题归结为:

The fact that lots of values you may write in the source, or read in with scanf or strtod, do not exist as floating point values and get silently converted to the nearest approximation. This is what demon9733's answer was talking about. The fact that many results get rounded due to not having enough precision to represent the actual result. An easy example where you can see this is adding x = 0x1fffffe and y = 1 as floats. Here, x has 24 bits of precision in the mantissa (ok) and y has just 1 bit, but when you add them, their bits are not in overlapping places, and the result would need 25 bits of precision. Instead, it gets rounded (to 0x2000000 in the default rounding mode). The fact that many results get rounded due to needing infinitely many places for the correct value. This includes both rational results like 1/3 (which you're familiar with from decimal where it takes infinitely many places) but also 1/10 (which also takes infinitely many places in binary, since 5 is not a power of 2), as well as irrational results like the square root of anything that's not a perfect square. Double rounding. On some systems (particularly x86), floating point expressions are evaluated in higher precision than their nominal types. This means that when one of the above types of rounding happens, you'll get two rounding steps, first a rounding of the result to the higher-precision type, then a rounding to the final type. As an example, consider what happens in decimal if you round 1.49 to an integer (1), versus what happens if you first round it to one decimal place (1.5) then round that result to an integer (2). This is actually one of the nastiest areas to deal with in floating point, since the behaviour of the compiler (especially for buggy, non-conforming compilers like GCC) is unpredictable. Transcendental functions (trig, exp, log, etc.) are not specified to have correctly rounded results; the result is just specified to be correct within one unit in the last place of precision (usually referred to as 1ulp).

When you're writing floating point code, you need to keep in mind what you're doing with the numbers that could cause the results to be inexact, and make comparisons accordingly. Often times it will make sense to compare with an "epsilon", but that epsilon should be based on the magnitude of the numbers you are comparing, not an absolute constant. (In cases where an absolute constant epsilon would work, that's strongly indicative that fixed point, not floating point, is the right tool for the job!)

编辑:特别地,相对大小的检查应该看起来像这样:

if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y))

FLT_EPSILON是float.h中的常量(将其替换为双精度的DBL_EPSILON或长双精度的LDBL_EPSILON), K是你选择的常量,这样你的计算的累积误差在最后一个地方肯定是由K个单位限制的(如果你不确定你得到的误差范围计算是正确的,让K比你的计算说的应该大几倍)。

最后,请注意,如果使用此方法,可能需要在接近零时进行一些特殊处理,因为FLT_EPSILON对于非法线没有意义。一个快速的解决方法是:

if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y) || fabs(x-y) < FLT_MIN)

如果使用double,同样替换DBL_MIN。

正确的问题是:如何在Cocoa Touch中比较分数?

正确答案:CGPointEqualToPoint()。

另一个问题:两个计算值是否相同?

答案贴在这里:他们不是。

如何检查它们是否接近?如果你想检查它们是否接近,那么不要使用CGPointEqualToPoint()。但是,不要检查它们是否接近。做一些在现实世界中有意义的事情,比如检查一个点是否超出了一条线,或者一个点是否在一个球体内。