objective-c 比较Objective-C中的浮点数的奇怪问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1614533/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Strange problem comparing floats in objective-C
提问by Dimitris
At some point in an algorithm I need to compare the float value of a property of a class to a float. So I do this:
在算法的某个时刻,我需要将类的属性的浮点值与浮点数进行比较。所以我这样做:
if (self.scroller.currentValue <= 0.1) {
}
where currentValue is a float property.
其中 currentValue 是一个浮点属性。
However, when I have equality and self.scroller.currentValue = 0.1the if statement is not fulfilled and the code not executed! I found out that I can fix this by casting 0.1 to float. Like this:
但是,当我具有相等性并且self.scroller.currentValue = 0.1if 语句未完成且代码未执行时!我发现我可以通过将 0.1 转换为浮动来解决这个问题。像这样:
if (self.scroller.currentValue <= (float)0.1) {
}
This works fine.
这工作正常。
Can anyone explain to my why this is happening? Is 0.1 defined as a double by default or something?
谁能向我解释为什么会发生这种情况?默认情况下 0.1 是否定义为双精度值?
Thanks.
谢谢。
回答by Georg Sch?lly
I believe, having not found the standard that says so, that when comparing a floatto a doublethe floatis cast to a doublebefore comparing. Floating point numbers without a modifier are considered to be doublein C.
我相信,具有不发现是这么说的,是一个比较时的标准float到double了float被转换为double比较之前。没有修饰符的浮点数被认为是double在 C 中。
However, in C there is no exact representation of 0.1 in floats and doubles. Now, using a float gives you a small error. Using a double gives you an even smaller error. The problem now is, that by casting the floatto a doubleyou carry over the bigger of error of the float. Of course they aren't gone compare equal now.
但是,在 C 中,浮点数和双精度数中没有 0.1 的精确表示。现在,使用浮点数会给您带来一个小错误。使用 double 会给你一个更小的错误。现在的问题是,通过将 强制转换float为 a,double您会携带float. 当然,他们现在还没有比较平等。
Instead of using (float)0.1you could use 0.1fwhich is a bit nicer to read.
(float)0.1您可以使用0.1fwhich 更易于阅读,而不是使用。
回答by James Snook
The problem is, as you have suggested in your question, that you are comparing a float with a double.
问题是,正如您在问题中所建议的那样,您正在将浮点数与双精度数进行比较。
There is a more general problem with comparing floats, this happens because when you do a calculation on a floating point number the result from the calculation may not be exactly what you expect. It is fairly common that the last bit of the resulting float will be wrong (although the inaccuracy can be larger than just the last bit). If you use ==to compare two floats then all the bits have to be the same for the floats to be equal. If your calculation gives a slightly inaccurate result then they won't compare equal when you expect them to. Instead of comparing the values like this, you can compare them to see if they are nearly equal. To do this you can take the positive difference between the floats and see if it is smaller than a given value (called an epsilon).
比较浮点数有一个更普遍的问题,这是因为当您对浮点数进行计算时,计算结果可能与您期望的不完全相同。结果浮点数的最后一位是错误的,这是很常见的(尽管误差可能比最后一位大)。如果您==用来比较两个浮点数,则所有位都必须相同才能使浮点数相等。如果您的计算给出了一个稍微不准确的结果,那么当您期望它们时,它们将不会比较相等。您可以比较它们以查看它们是否几乎相等,而不是像这样比较这些值。为此,您可以取浮点数之间的正差,看看它是否小于给定值(称为 epsilon)。
To choose a good epsilon you need to understand a bit about floating point numbers. Floating point numbers work similarly to representing a number to a given number of significant figures. If we work to 5 significant figures and your calculation results in the last digit of the result being wrong then 1.2345 will have an error of +-0.0001 whereas 1234500 will have an error of +-100. If you always base your margin of error on the value 1.2345 then your compare routine will be identical to ==for all values great than 10 (when using decimal). This is worse in binary, it's all values greater than 2. This means that the epsilon we choose has to be relative to the size of the floats that we are comparing.
要选择一个好的 epsilon,您需要对浮点数有所了解。浮点数的工作方式类似于将数字表示为给定数量的有效数字。如果我们计算 5 位有效数字,而您的计算结果的最后一位数字是错误的,那么 1.2345 的误差为 +-0.0001,而 1234500 的误差为 +-100。如果您始终将误差幅度基于值 1.2345,那么您的比较例程将与==所有大于 10 的值(使用十进制时)相同。这在二进制中更糟糕,所有值都大于 2。这意味着我们选择的 epsilon 必须与我们正在比较的浮点数的大小相关。
FLT_EPSILON is the gap between 1 and the next closest float. This means that it may be a good epsilon to choose if your number is between 1 and 2, but if your value is greater than 2 using this epsilon is pointless because the gap between 2 and the next nearest float is larger than epsilon. So we have to choose an epsilon relative to the size of our floats (as the error in the calculation is relative to the size of our floats).
FLT_EPSILON 是 1 和下一个最接近的浮点数之间的差距。这意味着如果您的数字介于 1 和 2 之间,选择它可能是一个很好的 epsilon,但如果您的值大于 2,则使用此 epsilon 毫无意义,因为 2 和下一个最近的浮点数之间的差距大于 epsilon。所以我们必须选择一个相对于浮点数大小的 epsilon(因为计算中的误差与浮点数的大小有关)。
A good(ish) floating point compare routine looks something like this:
一个好的(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;
}
This comparison routine compares floats relative to the size of the largest float passed in. scalbnf(1.0f, ilogb(a)) * FLT_EPSILONfinds the gap between aand the next nearest float. This is then multiplied by the epsilonMultiplier, so the size of the difference can be adjusted, depending on how inaccurate the result of the calculation is likely to be.
这个比较例程比较浮点数与传入的最大浮点数的大小。scalbnf(1.0f, ilogb(a)) * FLT_EPSILON找到a下一个最近的浮点数之间的差距。然后乘以epsilonMultiplier,因此可以调整差异的大小,具体取决于计算结果的不准确程度。
You can make a simple compareLessThanroutine like this:
你可以做一个这样的简单compareLessThan例程:
bool compareLessThan (float a, float b, unsigned epsilonMultiplier)
{
if (compareNearlyEqual (a, b, epsilonMultiplier)
return false;
return a < b;
}
You could also write a very similar compareGreaterThanfunction.
您也可以编写一个非常相似的compareGreaterThan函数。
It's worth noting that comparing floats like this may not always be what you want. For instance this will never find that a float is close to 0 unless it is 0. To fix this you'd need to decide what value you thought was close to zero, and write an additional test for this.
值得注意的是,像这样比较浮点数可能并不总是您想要的。例如,这永远不会发现浮点数接近 0,除非它是 0。要解决这个问题,您需要确定您认为接近零的值,并为此编写一个额外的测试。
Sometimes the inaccuracies you get won't depend on the size of the result of a calculation, but will depend on the values that you put into a calculation. For instance sin(1.0f + (float)(200 * M_PI))will give a much less accurate result than sin(1.0f)(the results should be identical). In this case your compare routine would have to look at the number you put into the calculation to know the margin of error of the answer.
有时,您获得的不准确度不取决于计算结果的大小,而是取决于您放入计算中的值。例如,sin(1.0f + (float)(200 * M_PI))将给出比sin(1.0f)(结果应该相同)更不准确的结果。在这种情况下,您的比较例程必须查看您放入计算中的数字才能知道答案的误差幅度。
回答by MarkPowell
Doubles and floats have different values for the mantissa store in binary (float is 23 bits, double 54). These will almost never be equal.
双精度数和浮点数在二进制尾数存储中具有不同的值(浮点数为 23 位,双数为 54)。这些几乎永远不会相等。
The IEEE Float Point article on wikipediamay help you understand this distinction.
维基百科上的 IEEE 浮点文章可能会帮助您理解这种区别。
回答by David Thornley
In C, a floating-point literal like 0.1 is a double, not a float. Since the types of the data items being compared are different, the comparison is done in the more precise type (double). In all implementations I know about, float has a shorter representation than double (usually expressed as something like 6 vs. 14 decimal places). Moreover, the arithmetic is in binary, and 1/10 does not have an exact representation in binary.
在 C 中,像 0.1 这样的浮点文字是双精度型,而不是浮点型。由于被比较的数据项的类型不同,比较以更精确的类型(double)进行。在我所知道的所有实现中,float 的表示比 double 短(通常表示为 6 位对 14 位小数)。此外,算术是二进制的,1/10 没有二进制的精确表示。
Therefore, you're taking a float 0.1, which loses accuracy, extending it to double, and expecting it to compare equal to a double 0.1, which loses less accuracy.
因此,您采用浮点数 0.1,这会降低精度,将其扩展为 double,并期望它与 double 0.1 相等,这会降低精度。
Suppose we were doing this in decimal, with float being three digits and double being six, and we were comparing to 1/3.
假设我们用十进制来做这件事,浮点数是三位数,双位数是六位数,我们正在与 1/3 进行比较。
We have the stored float value being 0.333. We're comparing it to a double with value 0.333333. We convert the float 0.333 to double 0.333000, and find it different.
我们存储的浮点值为 0.333。我们将它与值为 0.333333 的双精度值进行比较。我们将浮点数 0.333 转换为双精度 0.333000,发现不一样了。
回答by epatel
0.1 is actually a very dificult value to store binary. In base 2, 1/10 is the infinitely repeating fraction
0.1 实际上是一个很难存储二进制的值。在基数 2 中,1/10 是无限重复的分数
0.0001100110011001100110011001100110011001100110011...
As several has pointed out, the comparison has to made with a constant of the exact same precision.
正如一些人指出的那样,必须使用完全相同精度的常数进行比较。
回答by Lou Franco
Generally, in any language, you can't really count on equality of float-like types. In your case since it looks like you have more control, it does appear that 0.1 is not float by default. You could probably find that out with sizeof(0.1) (vs. sizeof(self.scroller.currentValue).
通常,在任何语言中,您都不能真正指望浮点类型的相等性。在您的情况下,由于您似乎拥有更多控制权,因此默认情况下 0.1 似乎不是浮动的。您可能会发现 sizeof(0.1)(与 sizeof(self.scroller.currentValue) 相比。
回答by Henry Sou
Convert it to a string, then compare:
将其转换为字符串,然后比较:
NSString* numberA = [NSString stringWithFormat:@"%.6f", a];
NSString* numberB = [NSString stringWithFormat:@"%.6f", b];
return [numberA isEqualToString: numberB];

