使用==比较Java中的浮点数有什么问题?

根据这个java.sun页面 ==是Java中浮点数的等式比较运算符。

但是,当我input这个代码:

 if(sectionID == currentSectionID) 

进入我的编辑器并运行静态分析,我得到:“JAVA0078浮点值与==相比较”

使用==来比较浮点值有什么问题? 什么是正确的方法来做到这一点?

testing花车为“平等”的正确方法是:

 if(Math.abs(sectionID - currentSectionID) < epsilon) 

其中epsilon是一个非常小的数字,如0.00000001,取决于所需的精度。

浮点值可以稍微偏离一点,所以它们可能不完全相等。 例如,将float设置为“6.1”,然后再打印出来,可能会得到一个类似“6.099999904632568359375”的报告值。 这是漂浮工作的基础。 因此,您不希望使用相等性比较它们,而是在一个范围内进行比较,也就是说,如果浮点数与要比较的数目的差值小于某个绝对值,

登记册上的这篇文章很好地概述了为什么是这样; 有用和有趣的阅读。

只是为了说明其他人所说的原因。

浮点数的二进制表示有点烦人。

在二进制中,大多数程序员知道1b = 1d,10b = 2d,100b = 4d,1000b = 8d

那么它的工作方式也是如此。

.1b = .5d,.01b = .25d,.001b = .125,…

问题是,没有确切的方法来表示大多数十进制数字,如.1,.2,.3等。所有你可以做的是近似的二进制。 当打印数字时,系统会做一些简单的修改,以便它显示.1而不是.10000000000001或.999999999999(它们可能与.1所存储的表示forms非常接近)

从评论编辑:这是一个问题的原因是我们的期望。 当我们把它转换成十进制时,我们完全期待2/3被篡改,不pipe是.7还是.67还是.666667 ..但是我们不会自动地预期.1和2/3一样被四舍五入 – 而这正是发生了什么事情。

顺便说一下,如果您好奇它内部存储的数字是一个纯二进制表示使用二进制“科学记数法”。 所以如果你告诉它存储十进制数10.75d,它将存储1010b为10,而.11b为十进制。 所以它会存储.101011,然后在最后保存几位:右移四位小数点。

(尽pipe从技术上讲它不再是小数点,但它现在已经是一个二元的观点了,但是对于大多数可以find这个答案的用户来说,这个术语不会让事情变得更容易理解。)

使用==来比较浮点值有什么问题?

因为0.1 + 0.2 == 0.3是不正确的

我认为在花车(和双打)方面有很多困惑,清理它是很好的。

  1. 符合标准的JVM [*]中使用浮点数作为ID本质上没有任何错误。 如果你简单地将float ID设置为x,那么不要做任何事情(即没有algorithm),然后testingy == x,你会没事的。 在使用它们作为HashMap中的键也没有什么错误。 你不能做的是假设x == (x - y) + y等等。这就是说,人们通常使用整数types作为ID,你可以观察到这里的大多数人被这个代码拖延了,所以对于实际的原因,最好遵守惯例。 请注意,有很多不同的double值,因为有很长的values ,所以你没有获得任何使用double 。 另外,生成“下一个可用的ID”对于双精度来说可能会很棘手,并且需要一些关于浮点algorithm的知识。 不值得的麻烦。

  2. 另一方面,依靠两个math等价计算的结果的数值相等是有风险的。 这是因为从十进制转换为二进制表示时舍入错误和精度损失。 在SO上讨论过这个问题。

[*]当我说“符合标准的JVM”时,我想排除某些大脑受损的JVM实现。 看到这个

舍入点值由于舍入误差而不可靠。

因此,它们可能不应该用作关键值,例如sectionID。 使用整数代替,或者如果int不包含足够的可能值,则为int

这是一个不是特定于java的问题。 使用==来比较两个浮点数/双精度数/任何十进制types数可能会因为存储方式而导致问题。 单精度浮点数(按照IEEE标准754)有32位,分布如下:

1位 – 符号(0 =正数,1 =负数)
8位 – 指数(2 ^ x中x的特殊(bias-127)表示)
23位 – Mantisa。 存储的真实号码。

mantisa是导致问题的原因。 这有点像科学记数法,只有基数2(二进制)的数字看起来像1.110011 x 2 ^ 5或类似的东西。 但是在二进制中,第一个1总是1(除了0的表示)

因此,为了节省一些内存空间(双关语),IEEE决定应该假设1。 例如,1011的mantisa确实是1.1011。

这可能会导致一些问题比较,特别是0,因为0不可能完全用浮点数表示。 这是除了其他答案所描述的浮点math问题之外,==不被鼓励的主要原因。

Java有一个独特的问题,因为这个语言在许多不同的平台上是通用的,每个平台都可以拥有自己独特的浮点格式。 这使得避免==更为重要。

比较两个花车(非语言特定的头脑)平等的正确方法如下:

 if(ABS(float1 - float2) < ACCEPTABLE_ERROR) //they are approximately equal 

其中ACCEPTABLE_ERROR是#defined或其他常数等于0.000000001或任何精度是必需的,正如维克多已经提到的。

有些语言有这个function,或者这个常量是内置的,但通常这是一个很好的习惯。

除了以前的答案,你应该知道有-0.0f+0.0f (它们是==但不equals )和Float.NaN (它是equals但不是== )相关的奇怪的行为(希望我'已经得到了正确的 – 呃,不要这样做!)。

编辑:我们来检查一下!

 import static java.lang.Float.NaN; public class Fl { public static void main(String[] args) { System.err.println( -0.0f == 0.0f); // true System.err.println(new Float(-0.0f).equals(new Float(0.0f))); // false System.err.println( NaN == NaN); // false System.err.println(new Float( NaN).equals(new Float( NaN))); // true } } 

欢迎来到IEEE / 754。

这里有一个很长的(但希望有用的)讨论关于这个和你可能遇到的许多其他浮点问题: 每个计算机科学家应该知道什么是浮点运算

首先,他们是浮动还是浮动? 如果其中一个是Float,则应该使用equals()方法。 此外,最好使用静态Float.compare方法。

你可以使用Float.floatToIntBits()。

 Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID) 

以下自动使用最好的精度:

 /** * Compare to floats for (almost) equality. Will check whether they are * at most 5 ULP apart. */ public static boolean isFloatingEqual(float v1, float v2) { if (v1 == v2) return true; float absoluteDifference = Math.abs(v1 - v2); float maxUlp = Math.max(Math.ulp(v1), Math.ulp(v2)); return absoluteDifference < 5 * maxUlp; } 

当然,您可以select多于或less于5个ULP(“最后一个单位”)。

如果您进入Apache Commons库,则Precision类具有epsilon和ULP的compareTo()equals()

你可能希望它是==,但123.4444444444443!= 123.4444444444442

如果你*必须使用浮动,strictfp关键字可能是有用的。

http://en.wikipedia.org/wiki/strictfp

产生相同实数的两个不同计算不一定产生相同的浮点数。 使用==来比较计算结果的人通常会因此而感到惊讶,所以这个警告有助于标记可能是微妙而难以重现错误的东西。

你正在处理的外包代码将使用浮动的东西名为sectionID和currentSectionID? 只是好奇。

@ Bill K:“float的二进制表示有点烦人。” 怎么会这样? 你会如何做得更好? 有些数字不能在任何基础上正确表示,因为它们永远不会结束。 Pi是一个很好的例子。 你只能接近它。 如果您有更好的解决scheme,请联系英特尔。

正确的方法是

 java.lang.Float.compare(float1, float2) 

正如其他答案中提到的,双打可以有小的偏差。 你可以写自己的方法来比较他们使用“可接受”的偏差。 不过…

有一个用于比较双打的apache类: org.apache.commons.math3.util.Precision

它包含一些有趣的常量: SAFE_MINEPSILON ,它们是简单算术运算的最大可能偏差。

它还提供了必要的方法来比较,平等或一轮双打。 (使用ulps或绝对偏差)

在一个答案我可以说,你应该使用:

 Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID) 

为了让你更好地学习如何正确使用相关的操作符,我在这里详细说明一些情况:一般来说,Java有三种方法来testingstring。 您可以使用==,.equals()或Objects.equals()。

他们有什么不同? ==testingstring中的参考质量意味着找出两个对象是否相同。 另一方面,.equals()从逻辑上testing两个string是否具有相同的值。 最后,Objects.equals()testing两个string中的任何空值,然后确定是否调用.equals()。

理想的运营商使用

那么这个问题呢经历了很多争论,因为三家运营商都有自己独特的优势和劣势。 例如,在比较对象引用时,==通常是首选选项,但也有可能会比较string值。

然而,你得到的是一个下降的价值,因为Java创造了一个错觉,你正在比较值,但在真正的意义上,你不是。 考虑下面的两种情况:

情况1:

 String a="Test"; String b="Test"; if(a==b) ===> true 

案例2:

 String nullString1 = null; String nullString2 = null; //evaluates to true nullString1 == nullString2; //throws an exception nullString1.equals(nullString2); 

所以,在testing为其devise的特定属性时,最好使用每个运算符。 但在几乎所有情况下,Object.equals()都是更普遍的操作符,因此Web开发人员可以select使用它。

在这里你可以得到更多的细节: http : //fluentthemes.com/use-compare-strings-java/

截至今天,快速简单的方法是:

 if (Float.compare(sectionID, currentSectionID) == 0) {...} 

然而,这些文档并没有明确说明浮点数的差值(来自@Victor答案的一个小数),但它应该是合理的,因为它是标准语言库的一部分。

然而,如果需要更高的或定制的精度,那么

 float epsilon = Float.MIN_NORMAL; if(Math.abs(sectionID - currentSectionID) < epsilon){...} 

是另一种解决scheme选项

减less舍入误差的一种方法是使用double而不是float。 这不会使问题消失,但它确实减less了程序中的错误数量,并且float几乎不是最好的select。 恕我直言。