奇怪的问题比较漂浮在Objective-C中
在algorithm的某个时刻,我需要将一个类的属性的float值与一个float进行比较。 所以我这样做:
if (self.scroller.currentValue <= 0.1) { }
其中currentValue是一个浮动属性。
但是,当我有相等和self.scroller.currentValue = 0.1
的if语句不履行和代码不执行! 我发现我可以通过投0.1来解决这个问题。 喜欢这个:
if (self.scroller.currentValue <= (float)0.1) { }
这工作正常。
任何人都可以解释为什么这是发生? 0.1被定义为一个默认的双重或东西?
谢谢。
我相信,没有find这样说的标准,当比较一个float
和一个double
float
时,在比较之前会抛出一个double
。 没有修饰符的浮点数被认为是C中的double
然而,在C中,在浮点数和双精度中没有0.1的精确表示。 现在,使用浮动给你一个小错误。 使用double会给你一个更小的错误。 现在的问题是,通过将float
在double
的位置,可以使float
的误差更大。 当然,他们现在不是相等的。
而不是使用(float)0.1
你可以使用0.1f
这是一个更好的阅读。
问题是,正如你在你的问题中所build议的那样,你正在比较一个float和一个double。
比较浮点数有一个更一般的问题,这是因为当你对浮点数进行计算时,计算结果可能并不是你所期望的。 结果浮点数的最后一位是错误的(尽pipe不精确度可能比最后一位大)。 如果你使用==
比较两个浮点数,那么所有的浮点数必须相同。 如果你的计算结果稍微不准确,那么当你期望他们的时候,他们不会相等。 不要比较这样的值,你可以比较它们,看它们是否几乎相等。 要做到这一点,你可以采取浮动之间的积极差异,看看它是否小于一个给定的值(称为epsilon)。
要select一个好的epsilon,你需要了解一些浮点数。 浮点数的作用类似于将数字表示为给定数量的有效数字。 如果我们对5位有效数字进行分析,并且结果的最后一位数字中的计算结果是错误的,那么1.2345的误差为+ -0.0001,而1234500的误差为+ -100。 如果您总是将误差范围设置为1.2345,则比较例程将与所有大于10的值(使用小数点时)相同。 这在二进制中更糟糕,它的值都大于2.这意味着我们select的epsilon必须相对于我们所比较的浮点数的大小。
FLT_EPSILON是1和下一个最接近的浮动之间的差距。 这意味着如果你的数字在1和2之间,select一个好的ε是可以的,但是如果你的数值大于2,那么使用这个ε是没有意义的,因为2和下一个最接近的浮点之间的差距大于ε。 所以我们必须select一个相对于我们浮点数的小数(因为计算中的误差是相对于浮点数的大小)。
一个好的(ish)浮点比较例程看起来像这样:
bool compareNearlyEqual (float a, float b, unsigned epsilonMultiplier) { float epsilon; /* May as well do the easy check first. */ if (a == b) return true; if (a > b) { epsilon = scalbnf(1.0f, ilogb(a)) * FLT_EPSILON * epsilonMultiplier; } else { epsilon = scalbnf(1.0, ilogb(b)) * FLT_EPSILON * epsilonMultiplier; } return fabs (a - b) <= epsilon; }
此比较例程比较浮动相对于传入的最大浮点数的大小scalbnf(1.0f, ilogb(a)) * FLT_EPSILON
finda
和下一个最接近的浮点之间的差距。 然后乘以epsilonMultiplier
,因此可以调整差异的大小,具体取决于计算结果可能有多不准确。
你可以像这样做一个简单的compareLessThan
例程:
bool compareLessThan (float a, float b, unsigned epsilonMultiplier) { if (compareNearlyEqual (a, b, epsilonMultiplier) return false; return a < b; }
你也可以写一个非常相似的compareGreaterThan
函数。
值得注意的是,比较像这样的花车可能并不总是你想要的。 例如,这将永远不会发现一个浮点数接近0,除非它是0.要解决这个问题,你需要决定你认为什么值接近于零,并为此写一个额外的testing。
有时你得到的不精确度不取决于计算结果的大小,而是取决于你计算的值。 例如, sin(1.0f + (float)(200 * M_PI))
会比sin(1.0f)
的结果准确得多(结果应该是相同的)。 在这种情况下,您的比较例程将不得不查看您计算的数字,以了解答案的误差范围。
双数和浮点数对于二进制的尾数存储有不同的值(浮点数是23位,双54)。 这几乎不会是平等的。
关于维基百科的IEEE Float Point文章可能会帮助你理解这个区别。
在C中,像0.1这样的浮点数是一个double,而不是float。 由于被比较的数据项的types是不同的,比较是以更精确的types(双)来完成的。 在我所知的所有实现中,float的表示比double更短(通常表示为6对14位小数)。 此外,算术是二进制的,1/10在二进制中没有确切的表示。
因此,您将采用0.1的浮点数,这会降低准确性,将其扩大一倍,并期望将其与双倍0.1进行比较,从而减less准确性。
假设我们用十进制来做这个,float是三位数字,double是六位数字,而我们是在比较1/3。
我们有存储的浮点值是0.333。 我们将它与价值0.333333的双倍数进行比较。 我们将浮点数0.333转换为0.333000加倍,并发现它不同。
0.1实际上是一个非常难以存储二进制值。 在基数2中,1/10是无限重复的分数
0.0001100110011001100110011001100110011001100110011...
正如几位指出的那样,这个比较必须以一个完全相同的精度进行。
一般来说,在任何语言中,你都不能指望floattypes的平等。 在你的情况,因为它看起来像你有更多的控制,它看起来0.1默认是不浮动。 你可能会发现sizeof(0.1)(sizeof(self.scroller.currentValue))。
将其转换为string,然后比较:
NSString* numberA = [NSString stringWithFormat:@"%.6f", a]; NSString* numberB = [NSString stringWithFormat:@"%.6f", b]; return [numberA isEqualToString: numberB];