在算法的某个时刻,我需要将类的属性的浮点值与浮点数进行比较。所以我这样做:
if (self.scroller.currentValue <= 0.1) {
}
其中currentValue是一个浮动属性。
但是,当我有相等且self.scroller.currentValue = 0.1
if语句未满足且代码未执行时!我发现我可以通过将0.1转换为浮动来解决这个问题。像这样:
if (self.scroller.currentValue <= (float)0.1) {
}
这很好用。
任何人都可以向我解释为什么会这样吗?默认情况下0.1是否定义为double?
感谢。
答案 0 :(得分:30)
我相信,如果没有找到标准,那么在比较float
和double
时,float
会在比较之前投放到double
。没有修饰符的浮点数在C中被视为double
。
然而,在C中,在浮点数和双精度数中没有精确的0.1表示。现在,使用float会给你一个小错误。使用double会给出更小的错误。现在的问题是,通过将float
转换为double
,您可以承担float
的更大错误。当然,现在他们并没有比较平等。
而不是使用(float)0.1
,您可以使用0.1f
来阅读更好的内容。
答案 1 :(得分:6)
问题在于,正如您在问题中所建议的那样,您正在将浮动与双重进行比较。
比较浮点数存在一个更普遍的问题,这是因为当您对浮点数进行计算时,计算结果可能与您预期的不完全相同。产生的浮点数的最后一位是错误的(虽然不准确度可能大于最后一位),这是相当普遍的。如果使用==
来比较两个浮点数,那么浮点数必须相同才能使浮点数相等。如果你的计算结果略微不准确,那么他们就不会在你期望的时候进行比较。您可以比较它们,看它们是否几乎相等,而不是比较这样的值。为此,您可以获取浮点数之间的正差异,并查看它是否小于给定值(称为epsilon)。
要选择一个好的epsilon,你需要了解浮点数。浮点数的作用类似于将数字表示给给定数量的有效数字。如果我们处理5个有效数字并且您的计算结果在结果的最后一个数字错误,则1.2345将具有+ -0.0001的错误,而1234500将具有+ -100的错误。如果始终将误差范围设置为值1.2345,则对于大于10的所有值(使用小数时),比较例程将与==
相同。这在二进制中更糟糕,它的所有值都大于2.这意味着我们选择的epsilon必须相对于我们正在比较的浮点数的大小。
FLT_EPSILON是1和下一个最接近的浮点数之间的差距。这意味着如果您的数字介于1和2之间,那么选择它可能是一个好的epsilon,但如果您的值大于2,则使用此epsilon是没有意义的,因为2和下一个最接近的float之间的差距大于epsilon。所以我们必须选择相对于浮点数大小的epsilon(因为计算中的误差是相对于浮点数的大小)。
一个好的(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
找到a
与下一个最近的浮点数之间的差距。然后将其乘以epsilonMultiplier
,因此可以调整差异的大小,具体取决于计算结果的不准确程度。
您可以像这样制作一个简单的compareLessThan
例程:
bool compareLessThan (float a, float b, unsigned epsilonMultiplier)
{
if (compareNearlyEqual (a, b, epsilonMultiplier)
return false;
return a < b;
}
你也可以写一个非常相似的compareGreaterThan
函数。
值得注意的是,比较像这样的花车可能并不总是你想要的。例如,除非它为0,否则永远不会发现浮点数接近于0.要解决此问题,您需要确定您认为接近于零的值,并为此编写一个额外的测试。
有时,您获得的不准确性取决于计算结果的大小,但取决于您在计算中输入的值。例如,sin(1.0f + (float)(200 * M_PI))
将得到比sin(1.0f)
更不准确的结果(结果应该相同)。在这种情况下,您的比较例程必须查看您在计算中输入的数字,以了解答案的误差范围。
答案 2 :(得分:4)
双精度和浮点数对于二进制的尾数存储具有不同的值(浮点数是23位,双精度54)。这些几乎永远不会是平等的。
The IEEE Float Point article on wikipedia可以帮助您理解这种区别。
答案 3 :(得分:4)
在C中,像0.1这样的浮点字面值是double,而不是float。由于被比较的数据项的类型不同,因此比较以更精确的类型(双)进行。在我所知的所有实现中,float具有比double更短的表示(通常表示为6比较小数位的14位)。此外,算术是二进制的,而1/10没有二进制的精确表示。
因此,你正在使用一个浮点数0.1,它会失去准确性,将其扩展到两倍,并且期望它比较等于双0.1,这会降低精度。
假设我们以十进制形式执行此操作,浮点数为三位数,双精度数为六位,我们将比较为1/3。
我们存储的浮点值为0.333。我们将它与价值为0.333333的双倍进行比较。我们将浮点数0.333转换为双0.333000,并发现它不同。
答案 4 :(得分:4)
0.1实际上是存储二进制文件非常困难的值。在基数2中,1/10是无限重复的分数
0.0001100110011001100110011001100110011001100110011...
正如有几位人士指出的那样,必须用完全相同精度的常数进行比较。
答案 5 :(得分:1)
通常,在任何语言中,您都不能真正依赖浮点类型的相等性。在你的情况下,因为它看起来你有更多的控制,它确实看起来默认情况下0.1不浮动。您可能会发现sizeof(0.1)(与sizeof(self.scroller.currentValue)相对应。
答案 6 :(得分:-1)
将其转换为字符串,然后比较:
NSString* numberA = [NSString stringWithFormat:@"%.6f", a];
NSString* numberB = [NSString stringWithFormat:@"%.6f", b];
return [numberA isEqualToString: numberB];