我知道UIKit使用CGFloat,因为它是分辨率无关的坐标系。
但每次我想检查frame.origin.x是否为0时,我都觉得很恶心:
if (theView.frame.origin.x == 0) {
// do important operation
}
当与==,<=,>=,<,>比较时,CGFloat是否容易出现假阳性?
它是一个浮点数,它们有不精确的问题:例如0.0000000000041。
Objective-C在比较时是否会在内部处理这个或者是否会发生原点。读作0的X不与0比较为真?
The last time I checked the C standard, there was no requirement for floating point operations on doubles (64 bits total, 53 bit mantissa) to be accurate to more than that precision. However, some hardware might do the operations in registers of greater precision, and the requirement was interpreted to mean no requirement to clear lower order bits (beyond the precision of the numbers being loaded into the registers). So you could get unexpected results of comparisons like this depending on what was left over in the registers from whoever slept there last.
也就是说,尽管我一看到它就努力删除它,但我工作的机构有大量使用gcc编译并运行在linux上的C代码,我们已经很长时间没有注意到这些意想不到的结果了。我不知道这是否是因为gcc为我们清除了低阶位,80位寄存器在现代计算机上不用于这些操作,标准已经改变了,还是什么。我想知道是否有人可以引用章节和诗句。
与0比较是安全的操作,只要0不是一个计算值(如上面的回答所述)。这样做的原因是0在浮点数中是一个完全可表示的数字。
谈到完全可表示的值,您可以在2的幂概念中获得24位范围(单精度)。所以12 4是完全可表示的,。5。25和。125也是。只要你所有重要的比特都是24比特的,你就是黄金。所以10.625可以被精确地表示出来。
这很好,但在压力下很快就会崩溃。我脑海中浮现出两种场景:
1)当涉及到计算时。不要相信√(3)*√(3)== 3。只是不会是那样的。它可能不会在一个范围内,就像其他答案暗示的那样。
2)当涉及任何非2的幂(NPOT)时。所以这听起来可能很奇怪,但是0.1是二进制的无限级数,因此任何涉及这样一个数字的计算从一开始就不精确。
(哦,原来的问题提到了与零的比较。不要忘记-0.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。