比较Objective-C中的浮点数的奇怪问题

Dim*_*ris 23 iphone floating-point casting objective-c

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

if (self.scroller.currentValue <= 0.1) {
}
Run Code Online (Sandbox Code Playgroud)

其中currentValue是一个浮动属性.

但是,当我具有相等性并且self.scroller.currentValue = 0.1if语句未满足且代码未执行时!我发现我可以通过将0.1转换为浮动来解决这个问题.像这样:

if (self.scroller.currentValue <= (float)0.1) {
}
Run Code Online (Sandbox Code Playgroud)

这很好用.

任何人都可以向我解释为什么会这样吗?默认情况下,0.1是否定义为double?

谢谢.

Geo*_*lly 30

我相信,具有不发现是这么说的,是一个比较时的标准floatdoublefloat被转换为double比较之前.没有修饰符的浮点数被认为是double在C.

但是,在C中,在浮点数和双精度数中没有精确的0.1表示.现在,使用float会给你一个小错误.使用double会给出更小的错误.现在的问题是,通过铸造float一个double你带来更大的错误float.当然,他们现在并没有比较平等.

而不是使用(float)0.1你可以使用0.1f哪个更好读.


Jam*_*ook 6

问题是,正如您在问题中所建议的那样,您正在将浮点数与double进行比较.

比较浮点数存在一个更普遍的问题,这是因为当您对浮点数进行计算时,计算结果可能与您预期的不完全相同.产生浮点数的最后一位是错误的(虽然不准确度可能大于最后一位),这是相当普遍的.如果您使用==比较两个浮点数,则所有位必须相同才能使浮点数相等.如果您的计算结果略有不准确,那么当您期望它们时,它们将不会相等.您可以比较它们,看它们是否几乎相等,而不是比较这样的值.为此,您可以获取浮动之间的正差异,并查看它是否小于给定值(称为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;
}
Run Code Online (Sandbox Code Playgroud)

此比较例程比较浮点数相对于传入的最大浮点数的大小.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;
}
Run Code Online (Sandbox Code Playgroud)

你也可以写一个非常相似的compareGreaterThan功能.

值得注意的是,比较像这样的花车可能并不总是你想要的.例如,除非它是0,否则永远不会发现浮点数接近于0.要解决此问题,您需要确定您认为接近于零的值,并为此编写一个额外的测试.

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