use*_*807 170 java equality floating-accuracy
根据这个java.sun页面 ==是Java中浮点数的相等比较运算符.
但是,当我输入以下代码时:
if(sectionID == currentSectionID)
Run Code Online (Sandbox Code Playgroud)
进入我的编辑器并运行静态分析,我得到:"JAVA0078浮点值与==相比"
使用==比较浮点值有什么问题?这样做的正确方法是什么?
Vic*_*tor 207
测试浮动"正确"的正确方法是:
if(Math.abs(sectionID - currentSectionID) < epsilon)
Run Code Online (Sandbox Code Playgroud)
其中epsilon是一个非常小的数字,如0.00000001,具体取决于所需的精度.
Bil*_*l K 22
只是为了说明其他人所说的理由.
浮点的二进制表示有点烦人.
在二进制中,大多数程序员知道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 ..但我们不会自动期望.1以与2/3相同的方式舍入 - 而这正是发生的事情.
顺便说一句,如果你很好奇,它内部存储的数字是使用二进制"科学记数法"的纯二进制表示.因此,如果你告诉它存储十进制数10.75d,它将为10存储1010b,为十进制存储.11b.所以它会存储.101011然后它会在末尾保存几个位来说:将小数点向右移动四个位置.
(虽然从技术上讲它不再是小数点,但它现在是一个二进制点,但是对于大多数能够找到任何用途答案的人来说,这个术语不会让事情变得更容易理解.)
Aak*_*shM 19
使用==比较浮点值有什么问题?
因为这不是真的 0.1 + 0.2 == 0.3
qua*_*dev 12
我认为浮游物(和双打)之间存在很多混淆,清除它是很好的.
在标准兼容的JVM [*] 中使用浮点数作为ID没有任何内在错误.如果你只是将浮点数ID设置为x,不做任何事情(即没有算术),然后测试y == x,你会没事的.将它们用作HashMap中的键也没有错.你不能做的是假设x == (x - y) + y等等等等.这就是说,人们通常使用整数类型作为ID,你可以观察到这里的大多数人被这个代码推迟了,所以出于实际原因,最好遵守惯例.请注意,有很多不同的double值,因为有很长的values,所以你通过使用没有任何收获double.此外,生成"下一个可用ID"对于双精度来说可能很棘手,并且需要一些浮点运算知识.不值得麻烦.
另一方面,依赖于两个数学上等价的计算的结果的数值相等是有风险的.这是因为从十进制表示转换为二进制表示时出现舍入误差和精度损失.这已经在SO上讨论过死亡.
[*]当我说"符合标准的JVM"时,我想排除某些受到大脑损坏的JVM实现.看到这个.
由于舍入误差,起泡点值不可靠.
因此,它们可能不应该用作键值,例如sectionID.改为使用整数,或者long如果int不包含足够的可能值.
这是一个不是java特有的问题.使用==比较两个浮点数/双精度数/任何十进制类型数可能会因为它们的存储方式而导致问题.单精度浮点数(根据IEEE标准754)具有32位,分布如下:
1位 - 符号(0 =正,1 =负)
8位 - 指数(2 ^ x中x的特殊(偏差-127)表示)
23位 - Mantisa.存储的实际编号.
mantisa是导致问题的原因.它有点像科学记数法,只有基数2(二进制)中的数字看起来像1.110011 x 2 ^ 5或类似的东西.但在二进制中,第一个1总是1(除了0的表示)
因此,为了节省一点内存空间(双关语),IEEE决定应该假设1.例如,1011的mantisa确实是1.1011.
这可能会导致一些比较问题,特别是0,因为0不能在float中准确表示.除了其他答案描述的浮点数学问题之外,这是阻止==的主要原因.
Java有一个独特的问题,即该语言在许多不同的平台上都是通用的,每个平台都有自己独特的浮点格式.这使得避免==更为重要.
比较两个浮点数(非语言特定的头脑)的正确方法如下:
if(ABS(float1 - float2) < ACCEPTABLE_ERROR)
//they are approximately equal
Run Code Online (Sandbox Code Playgroud)
其中ACCEPTABLE_ERROR是#defined或其他常量等于0.000000001或者需要的精度,正如Victor提到的那样.
有些语言具有此功能或内置的常量,但通常这是一个很好的习惯.
截至今天,快速简便的方法是:
if (Float.compare(sectionID, currentSectionID) == 0) {...}
Run Code Online (Sandbox Code Playgroud)
但是,文档 并没有明确指出余量差异的值( 来自@Victor答案的epsilon),它总是存在于浮点数的计算中,但它应该是合理的,因为它是标准语言库的一部分.
然而,如果需要更高或定制的精度,那么
float epsilon = Float.MIN_NORMAL;
if(Math.abs(sectionID - currentSectionID) < epsilon){...}
Run Code Online (Sandbox Code Playgroud)
是另一种解决方案.
除了以前的答案,你应该知道有一些奇怪的行为-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
}
}
Run Code Online (Sandbox Code Playgroud)
欢迎来到IEEE/754.
| 归档时间: |
|
| 查看次数: |
150252 次 |
| 最近记录: |