==
是浮点数的相等比较运算符。然而,当我输入以下代码时:
if(sectionID == currentSectionID)
当我在编辑器中运行静态分析时,我收到了以下提示:"JAVA0078 浮点数使用“==”进行比较"
使用 ==
比较浮点数有什么问题? 正确的方法是什么?
==
是浮点数的相等比较运算符。if(sectionID == currentSectionID)
当我在编辑器中运行静态分析时,我收到了以下提示:"JAVA0078 浮点数使用“==”进行比较"
使用 ==
比较浮点数有什么问题? 正确的方法是什么?
测试浮点数“相等”的正确方法是:
if(Math.abs(sectionID - currentSectionID) < epsilon)
epsilon是一个非常小的数,例如0.00000001,具体取决于所需的精度。
if(Math.abs(sectionID - currentSectionID) < epsilon*sectionID
来解决这个问题? - enthusiasticgeekMath.ulp()
。 - Michael Piefel浮点数值可能存在微小误差,因此它们可能不会报告为完全相等。例如,将一个浮点型设置为“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差不多)。
编辑自评论:问题在于我们的期望值。我们完全预计2/3在转换为十进制时会进行一些虚假舍入,例如.7或.67或.666667... 但我们并不会自动预期会像2/3一样虚假舍入.1——但事实上就是这样。
顺便提一下,如果你好奇,它内部存储的数字是使用二进制“科学记数法”表示的纯二进制表示形式。因此,如果您告诉它存储10.75d的十进制数,它将为10存储1010b,对于小数点则存储.11b。因此它会存储.101011然后在末尾保存一些位以指示:将小数点向右移四个位置。
(尽管从技术上讲,它已经不再是十进制点了,现在是一个二进制点,但这种术语对大多数找到本答案有用的人来说不会使事情更易懂。)
Float.compare(0.1f+0.2f, 0.3f) == 0
怎么样? - Aquarius Power截至今日,快速方便的方法是:
if (Float.compare(sectionID, currentSectionID) == 0) {...}
float epsilon = Float.MIN_NORMAL;
if(Math.abs(sectionID - currentSectionID) < epsilon){...}
还有另一种解决方案可供选择。
(sectionId == currentSectionId)
相同,但对于浮点数来说并不准确。epsilon方法是更好的方法,可以参考这个答案:https://dev59.com/y3NA5IYBdhLWcg3wHp6B#1088271 - typoerrpr我认为在浮点数(和双精度浮点数)方面存在很多混淆,有必要澄清。
在标准兼容的 JVM 中使用浮点数作为 ID 是没有任何本质问题的[*]。如果你只是将浮点数 ID 设置为 x,不对其进行任何操作(即不进行算术运算),然后测试 y 是否等于 x,那么就没问题了。同样,将它们用作 HashMap 中的键也没有问题。但是你不能假设等式成立,例如 x == (x - y) + y
等等。尽管如此,人们通常使用整数类型作为 ID,你可以观察到这里的大多数人对此代码不感冒,因此出于实际原因,最好遵循惯例。请注意,与 long 值一样,有许多不同的 double 值,因此使用 double 没有任何更多的好处。此外,使用双精度浮点数生成“下一个可用 ID”可能会很棘手,并需要一些关于浮点数算术的知识。得不偿失。
另一方面,依靠两个在数学上等价的计算结果的数值相等性是有风险的。这是由于从十进制转换为二进制表示时出现的舍入误差和精度损失。这在 SO 上已经讨论过很多次了。
[*] 当我说“标准兼容的 JVM”时,我想排除某些有缺陷的 JVM 实现。请参见 这里。
float
值表并使用==
进行比较,可怕的IEEE-754比较规则将导致表中充满了NaN
值。 - supercatequals
实例方法,而是指比较两个float
类型值的静态方法(我认为在Float
类中)。 - supercat==
,则会得到相同的结果。您必须自己拒绝NaN。 - quant_devif(ABS(float1 - float2) < ACCEPTABLE_ERROR)
//they are approximately equal
其中ACCEPTABLE_ERROR被定义为0.000000001或其他所需的精度常量,正如Victor已经提到的。
有些语言内置了此功能或常量,但通常养成这种习惯是很好的。
浮点数值不可靠,因为存在舍入误差。
因此,它们可能不适合用作关键值,例如sectionID。请改用整数,如果int不能包含足够的可能值,则使用long。
double
类型更加精确,但它们也是浮点数值,因此我的答案旨在包括float
和double
两者。 - Eric Wilson除了之前的答案,你应该知道与-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
}
}
==
不意味着数字是“位相同的”(尽管同一个数字可以用不同的位模式表示,但只有其中一个是规范化形式)。此外,-0.0f
和 0.0f
由不同的位模式表示(符号位不同),但使用 ==
比较相等(但不使用 equals
)。一般来说,您认为 ==
是按位比较的假设是错误的。 - Pavel Minaev