我知道UIKit使用CGFloat,因为它是分辨率无关的坐标系。
但每次我想检查frame.origin.x是否为0时,我都觉得很恶心:
if (theView.frame.origin.x == 0) {
// do important operation
}
当与==,<=,>=,<,>比较时,CGFloat是否容易出现假阳性?
它是一个浮点数,它们有不精确的问题:例如0.0000000000041。
Objective-C在比较时是否会在内部处理这个或者是否会发生原点。读作0的X不与0比较为真?
[“正确答案”掩盖了选择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。
因为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值可能是极其危险的:想想航空电子设备、武器制导、发电厂操作、车辆导航,几乎所有计算与现实世界相结合的应用。
对《愤怒的小鸟》来说,没有那么危险。