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

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

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

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

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


当前回答

你可以使用这样的代码来比较float和0:

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

这将与0.1的精度进行比较,在这种情况下,这对CGFloat来说足够了。

其他回答

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。

我认为正确的做法是将每个数字声明为一个对象,然后在该对象中定义三个东西:1)相等运算符。2)一个setAcceptableDifference方法。3)价值本身。如果两个值的绝对差小于设置为可接受的值,则相等运算符返回true。

您可以对对象进行子类化以适应该问题。例如,如果1到2英寸之间的圆金属棒的直径相差小于0.0001英寸,则可以认为它们的直径相等。因此,您可以使用参数0.0001调用setAcceptableDifference,然后放心地使用相等操作符。

你可以使用这样的代码来比较float和0:

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

这将与0.1的精度进行比较,在这种情况下,这对CGFloat来说足够了。

-(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也是一个完全有效的浮点值。)