根据 这个 java.sun 页面==是 Java 中浮点数的相等比较运算符。
==
但是,当我键入这些代码时:
if(sectionID == currentSectionID)
进入我的编辑器并运行静态分析,我得到: “ JAVA0078浮点值比较 = =”
使用 ==比较浮点值有什么问题吗? 正确的方法是什么
浮点值可能会偏离一点,因此它们的报告可能不完全相等。例如,将浮点数设置为“6.1”,然后再次打印出来,您可能会得到类似于“6.09999904632568359375”的报告值。这是浮点数工作方式的基础; 因此,您不想使用等式来比较它们,而是在一个范围内进行比较,也就是说,如果浮点数的 diff 小于您想要比较的数字的某个绝对值。
登记册上的这篇 文章很好地概述了为什么会出现这种情况; 阅读这篇文章既有用又有趣。
首先,它们是浮动的还是浮动的?如果其中之一是 Float,则应该使用 equals ()方法。另外,最好使用静态 Float.ratio 方法。
由于舍入误差,浮点数值不可靠。
因此,它们可能不应该用作键值,如 sectionID。如果 int不包含足够的可能值,则使用整数或 long。
int
long
正确的方法是
java.lang.Float.compare(float1, float2)
测试 float 是否“平等”的正确方法是:
if(Math.abs(sectionID - currentSectionID) < epsilon)
其中 ε 是一个非常小的数字,如0.00000001,取决于所需的精度。
你可能希望它是 = = ,但是123.444444444443! = 123.44444444444442
除了以前的答案,你应该意识到有一些奇怪的行为与 -0.0f和 +0.0f(它们是 ==但不是 equals)和 Float.NaN(它是 equals但不是 ==)(希望我说对了,啊,不要这样做.
-0.0f
+0.0f
equals
Float.NaN
编辑: 让我们检查一下!
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。
这里有一个非常长(但希望有用)的讨论,关于这个问题和许多其他浮点问题,您可能会遇到: 关于浮点算法,每个计算机科学家都应该知道什么
只是为了说明其他人所说的背后的原因。
浮点数的二进制表示有点烦人。
在二进制中,大多数程序员知道1b = 1d,10b = 2d,100b = 4d,1000b = 8d 之间的相关性
反之亦然。
.1 b = .5 d,.01 b = .25 d,.001 b = .125,...
问题是没有精确的方法来表示大多数的十进制数,比如.1、 .2、 .3等等。你所能做的就是用二进制近似。当数字打印出来以便显示.1时,系统会进行一些修改,而不是.100000000000001或.999999999999(它们可能与.1的存储表示形式一样接近)
编辑从评论: 这是一个问题的原因是我们的期望。当我们把它转换成十进制时,我们完全期望在某个时刻2/3被篡改,无论是.7或.67或.66667。.但是我们不会自动地期望.1和2/3以同样的方式四舍五入——这就是正在发生的事情。
顺便说一句,如果你好奇的话,它内部存储的数字是一个纯粹的二进制表示,使用一个二进制“科学记数法”。如果你让它存储十进制数10.75 d 它会存储十进制数1010b 和十进制数0.11 b。所以它会存储。101011然后它节省了一些位在最后说: 移动小数点四位的权利。
(虽然从技术上讲,它不再是一个小数点,现在它是一个二进制点,但是这个术语不会让大多数人更容易理解这个问题,因为他们会发现这个答案没有任何用处。)
使用 = = 比较浮点值有什么错?
因为 0.1 + 0.2 == 0.3不是真的
0.1 + 0.2 == 0.3
两个不同的计算产生相等的实数并不一定产生相等的浮点数。使用 = = 比较计算结果的人通常会对此感到惊讶,因此这个警告有助于标记可能是一个微妙且难以重现的 bug。
您是否正在处理外包代码,这些代码将对名为 sectionID 和 currentSectionID 的内容使用 float。
@ Bill K: “浮点数的二进制表示有点烦人。”为什么?你要怎么做才能做得更好?有些数字无法用任何基数来表示,因为它们永远不会结束。圆周率就是一个很好的例子。你只能接近它。如果你有更好的解决方案,联系英特尔。
这不是 Java 特有的问题。使用 = = 比较两个浮点数/双精度数/任何十进制类型的数字可能会因为它们的存储方式而导致问题。 单精度浮点数(根据 IEEE 标准754)有32位,分布如下:
1位-符号(0 = 正,1 = 负) 8位-指数(2 ^ x 中 x 的特殊(偏置 -127)表示) 23位-Mantisa。实际存储的数字。
是螳螂导致了这个问题。它有点像科学记数法,只不过以2为基数(二进制)的数字看起来像1.110011 x 2 ^ 5或类似的数字。 但是在二进制中,前1总是1(0的表示形式除外)
因此,为了节省一点内存空间(双关语) ,IEEE 决定假设1。例如,1011的螳螂实际上是1.1011。
这可能会导致一些比较问题,特别是0,因为0不可能精确地表示为浮点数。 这是不鼓励使用 = = 的主要原因,另外还有其他答案所描述的浮点数学问题。
Java 有一个独特的问题,那就是该语言在许多不同的平台上都是通用的,每个平台都有自己独特的 float 格式。这使得避免 = = 变得更加重要。
比较两个浮点数(不是特定于语言的)是否相等的正确方法如下:
if(ABS(float1 - float2) < ACCEPTABLE_ERROR) //they are approximately equal
其中 ACCEPTABLE _ ERROR 是 # 定义的,或者其他等于0.00000001的常数,或者任何需要的精度,就像 Victor 已经提到的那样。
有些语言内置了这个功能或这个常量,但通常这是一个好习惯。
我认为对于 float (和 double)有很多困惑,最好把它们弄清楚。
使用浮动作为 ID 在标准兼容的 JVM 中[ * ]本身并没有什么错误。如果您只是简单地将浮点 ID 设置为 x,那么不要对它进行任何操作(即不用算术) ,稍后测试 y = = x,就可以了。在 HashMap 中使用它们作为键也没有什么错。你不能做的就是假设像 x == (x - y) + y这样的等式。也就是说,人们通常使用整数类型作为 ID,您可以观察到,这里的大多数人都不喜欢这种代码,因此出于实际原因,最好遵守约定。请注意,double的值与长 values的值一样多,因此使用 double没有任何好处。另外,使用双精度浮点数生成“下一个可用 ID”可能比较棘手,并且需要对浮点数算法有一定的了解。不值得这么麻烦。
x == (x - y) + y
double
values
另一方面,依赖于两个数学等效计算结果的数值相等是有风险的。这是由于舍入误差和精度损失时,从十进制转换为二进制表示。这个问题已经讨论到死了。
当我说“标准兼容的 JVM”时,我想排除某些脑损伤的 JVM 实现。
减少舍入误差的一种方法是使用 double 而不是 float。这不会使问题消失,但它确实减少了程序中的错误数量,而 float 几乎从来都不是最佳选择。恕我直言。
如果您 * 必须 * 使用 floats,则 strictfp 关键字可能非常有用。
Http://en.wikipedia.org/wiki/strictfp
可以使用 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; }
当然,您可能会选择多于或少于5个 ULP (“单位在最后一个地方”)。
如果您使用 ApacheCommons 库,则 Precision类具有带有 epsilon 和 ULP 的 compareTo()和 equals()。
Precision
compareTo()
equals()
正如在其他答案中提到的,双精度浮点数可以有很小的偏差。你可以写自己的方法来比较他们使用一个“可接受的”偏差。但是..。
有一个 apache 类用于比较双精度浮点数: Org.apache.commons. math3.util 精度
它包含一些有趣的常量: SAFE_MIN和 EPSILON,它们是简单算术运算的最大可能偏差。
SAFE_MIN
EPSILON
它还提供了必要的方法来比较,等于或圆双打。(使用 ulps 或平均差)
在一行答案中,我可以说,你应该使用:
为了让你更好地了解如何正确使用相关运算符,我在这里阐述一些例子: 一般来说,有三种方法可以在 Java 中测试字符串: = = ,. equals ()或 Objects.equals ()。
他们有什么不同?= = 测试字符串中的引用质量,这意味着找出两个对象是否相同。另一方面。Equals ()测试两个字符串在逻辑上是否具有相等的值。最后,Objects.equals ()测试两个字符串中的空值,然后确定是否调用。等于()。
使用的理想操作符
这个问题已经引起了很多争论,因为这三个运营商各有各的优缺点。在比较对象引用时,= = 通常是首选选项,但在某些情况下,它似乎也会比较字符串值。
但是,您得到的是一个 fall 值,因为 Java 创建了一种错觉,即您正在比较值,但实际上并非如此。考虑以下两种情况:
String a="Test"; String b="Test"; if(a==b) ===> true
String nullString1 = null; String nullString2 = null; //evaluates to true nullString1 == nullString2; //throws an exception nullString1.equals(nullString2);
因此,在测试特定属性时,最好使用每个操作符。但是在几乎所有的情况下,Objects.equals ()是一个更加通用的操作符,因此 web 开发人员会选择它。
在这里你可以得到更多的细节: http://fluentthemes.com/use-compare-strings-java/
从今天开始,最简单快捷的方法就是:
if (Float.compare(sectionID, currentSectionID) == 0) {...}
然而,医生并没有明确指定浮点数计算中总是存在的边际差值(来自@Victor 答案的 Epsilon) ,但它应该是合理的,因为它是标准语言库的一部分。
然而,如果需要更高的或定制的精度,那么
float epsilon = Float.MIN_NORMAL; if(Math.abs(sectionID - currentSectionID) < epsilon){...}
是另一个解决方案。