Iphone objective-C中比较浮点的奇怪问题

Iphone objective-C中比较浮点的奇怪问题,iphone,objective-c,casting,floating-point,Iphone,Objective C,Casting,Floating Point,在算法中的某个时刻,我需要将类属性的浮点值与浮点值进行比较。所以我这样做: if (self.scroller.currentValue <= 0.1) { } 这个很好用 有人能向我解释为什么会这样吗?默认情况下,0.1是否定义为双精度 谢谢。通常,在任何语言中,您都不能真正依赖于类浮点类型的相等性。在您的情况下,因为看起来您有更多的控制权,所以0.1在默认情况下似乎不是浮动的。您可能会发现sizeof(0.1)(与sizeof(self.scroller.currentValue)相

在算法中的某个时刻,我需要将类属性的浮点值与浮点值进行比较。所以我这样做:

if (self.scroller.currentValue <= 0.1) {
}
这个很好用

有人能向我解释为什么会这样吗?默认情况下,0.1是否定义为双精度


谢谢。

通常,在任何语言中,您都不能真正依赖于类浮点类型的相等性。在您的情况下,因为看起来您有更多的控制权,所以0.1在默认情况下似乎不是浮动的。您可能会发现sizeof(0.1)(与sizeof(self.scroller.currentValue)相比)。

双精度浮点和浮点数在二进制尾数存储中有不同的值(浮点数是23位,双精度浮点是54位)。它们几乎永远不会相等


可能有助于您理解这种区别。

我认为,由于没有找到这样的标准,在比较
浮点数
双精度
时,
浮点数
在比较之前被转换为
双精度
。在C中,不带修饰符的浮点数被视为
双精度

然而,在C语言中,0.1在浮点数和双精度中没有精确的表示。现在,使用浮点数会产生一个小错误。使用双精度会产生更小的错误。现在的问题是,通过将
浮点数
转换为
双精度
,你会继承
浮点数
中较大的错误。当然,它们不会消失我们现在是平等的


与使用
(float)0.1
不同,您可以使用
0.1f
,这更易于阅读。

在C中,像0.1这样的浮点文本是双精度的,而不是浮点值。由于被比较的数据项的类型不同,因此比较是以更精确的类型(double)进行的.在我所知道的所有实现中,float的表示形式都比double短(通常表示为6位和14位小数)。此外,算术是二进制的,1/10没有二进制的精确表示形式

因此,您将浮点值取为0.1,这会降低精度,将其扩展为双倍,并期望它与双倍值0.1进行比较,这会降低精度

假设我们用十进制进行计算,浮点数是三位数,双位数是六位数,我们比较的是1/3


我们存储的浮点值是0.333。我们将其与值为0.333333的双精度浮点值进行比较。我们将浮点值0.333转换为双精度浮点值0.333000,发现它不同。

0.1实际上很难存储二进制。在基数2中,1/10是无限重复的分数

0.0001100110011001100110011001100110011001100110011...

正如一些人指出的那样,比较必须使用一个精度完全相同的常数。

问题是,正如您在问题中所建议的,您正在比较浮点和双精度

比较浮点数有一个更普遍的问题,这是因为当您对浮点数进行计算时,计算的结果可能与您预期的不完全一致。结果浮点数的最后一位出错(尽管不准确度可能大于最后一位)是很常见的。如果使用
==
比较两个浮点值,则所有位必须相同,才能使浮点值相等。如果计算结果稍有不准确,则它们不会在您期望的情况下进行相等比较。您可以通过比较来确定它们是否接近相等,而不是像这样比较值。为此,您可以他计算浮点数之间的正差值,看看它是否小于给定值(称为ε)

要选择一个好的ε,您需要了解一点浮点数。浮点数的工作原理类似于将一个数字表示为给定的有效数字。如果我们计算了5个有效数字,并且您的计算结果在结果的最后一位是错误的,那么1.2345将有+-0.0001的错误,其中as 1234500的误差为+-100。如果您总是将误差幅度基于值1.2345,则对于大于10的所有值(使用十进制时),您的比较例程将与
=
相同。这在二进制中更糟糕,它的所有值都大于2。这意味着我们选择的ε必须与我们正在比较的浮点的大小相关

FLT_EPSILON是1和下一个最近的浮点之间的间隔。这意味着如果你的数字在1和2之间,那么选择它可能是一个好的EPSILON,但是如果你的值大于2,那么使用这个EPSILON是没有意义的,因为2和下一个最近的浮点之间的间隔大于EPSILON。所以我们必须选择一个相对于大小o的EPSILONf我们的浮点数(因为计算中的误差与浮点数的大小有关)

一个好的(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;
}
bool compareLessThan (float a, float b, unsigned epsilonMultiplier)
{
  if (compareNearlyEqual (a, b, epsilonMultiplier)
    return false;

  return a < b;
}
您还可以编写一个非常类似的
compareGreaterThan
函数

值得注意的是,比较这样的浮点值可能并不总是您想要的。例如,除非浮点值为0,否则这将永远不会发现浮点值接近0。要解决此问题,您需要确定您认为接近0的值,并为此编写额外的测试


有时,您得到的不准确度并不取决于计算结果的大小,而是取决于您在计算中输入的值。例如,
sin(1.0f+(float)(200*M_PI))
得到的结果比
sin(1.0f)
(结果应该是相同的)。在这种情况下,您的比较例程必须查看您在计算中输入的数字,以了解答案的误差幅度。

将其转换为字符串,然后比较:

NSString* numberA = [NSString stringWithFormat:@"%.6f", a];
NSString* numberB = [NSString stringWithFormat:@"%.6f", b];

return [numberA isEqualToString: numberB];

sizeof揭示了0.1是一个双精度数。当两个值都为0.10000时,你得不到相等的结果仍然很奇怪,不是吗?@Dimitris,没那么奇怪。请看@MarkPowell的答案。@Dimitris:不,这并不奇怪。0.1不能用二进制精确表示。正如
NSString* numberA = [NSString stringWithFormat:@"%.6f", a];
NSString* numberB = [NSString stringWithFormat:@"%.6f", b];

return [numberA isEqualToString: numberB];