45 Float.NaN == Float.NaN 为 false 是怎么实现的?
Posted 蓝风9
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了45 Float.NaN == Float.NaN 为 false 是怎么实现的?相关的知识,希望对你有一定的参考价值。
前言
呵呵 这是很久之前 看 jls 的时候就存在的疑惑, 当时写了 case 来看, 果然结论 和 jls 的规范是一致的
但是 从来没有思考过 为什么会这样, 然后 具体又是 怎么实现的 ?
回溯一下用例, 可以回溯到 2019 年, 但是 实际的应该还有 这个问题的更早的用例
呵呵 今天就来看一下
/**
* Test12FloatEquals
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2019-08-23 15:21
*/
public class Test12FloatEquals
// Test12FloatEquals
public static void main(String[] args)
System.out.println(Float.NaN == Float.NaN);
System.out.println(new Float(Float.NaN).equals(new Float(Float.NaN)));
System.out.println(+0.0f == -0.0f);
System.out.println(new Float(+0.0f).equals(new Float(-0.0f)));
以下代码的截图, 调试基于 jdk8
测试用例
/**
* Test12FloatEquals
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2021-10-31 10:03
*/
public class Test12FloatEquals
// Test12FloatEquals
public static void main(String[] args) throws Exception
float NaN = 0.0f / 0.0f;
float Max = 1.0f / 0.0f;
float Min = -1.0f / 0.0f;
boolean equals01 = Float.NaN == Float.NaN;
boolean equals02 = NaN == NaN;
boolean equals03 = NaN != NaN;
boolean equals04 = Max == Max;
boolean equals05 = Min == Min;
System.in.read();
然后结果如下, 我们这里主要关注的是 NaN 相关的计算
反编译得到字节码相关信息, 这里直接使用 Float.NaN 的计算是被常量折叠了, 然后 "自己计算得" NaN 是没有被折叠
master:test12 jerry$ javap -c Test12FloatEquals.class
Compiled from "Test12FloatEquals.java"
public class com.hx.test12.Test12FloatEquals
public com.hx.test12.Test12FloatEquals();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: ldc #2 // float NaNf
2: fstore_1
3: ldc #3 // float Infinityf
5: fstore_2
6: ldc #4 // float -Infinityf
8: fstore_3
9: iconst_0
10: istore 4
12: fload_1
13: fload_1
14: fcmpl
15: ifne 22
18: iconst_1
19: goto 23
22: iconst_0
23: istore 5
25: fload_1
26: fload_1
27: fcmpl
28: ifeq 35
31: iconst_1
32: goto 36
35: iconst_0
36: istore 6
38: fload_2
39: fload_2
40: fcmpl
41: ifne 48
44: iconst_1
45: goto 49
48: iconst_0
49: istore 7
51: fload_3
52: fload_3
53: fcmpl
54: ifne 61
57: iconst_1
58: goto 62
61: iconst_0
62: istore 8
64: getstatic #6 // Field java/lang/System.in:Ljava/io/InputStream;
67: invokevirtual #7 // Method java/io/InputStream.read:()I
70: pop
71: return
jls 关于 NaN 计算的说明
4.2.3. Floating-Point Types, Formats, and Values
可以看到的是 我们上面的 NaN 的 "==", "!=" 的结果是符合规范的描述的
另外在 jvms 里面 float 提供了两个操作的字节码
差异仅仅在于 NaN 的情况
Float.NaN == Float.NaN 的常量折叠
看一下 javac 里面对于上面 Float.NaN == Float.NaN 的处理
Both value1 and value2 must be of type
float
. The values are popped from the operand stack and undergo value set conversion (§2.8.3), resulting in value1' and value2'. A floating-point comparison is performed:
If value1' is greater than value2', the
int
value 1 is pushed onto the operand stack.Otherwise, if value1' is equal to value2', the
int
value 0 is pushed onto the operand stack.Otherwise, if value1' is less than value2', the
int
value -1 is pushed onto the operand stack.Otherwise, at least one of value1' or value2' is NaN. The fcmpg instruction pushes the
int
value 1 onto the operand stack and the fcmpl instruction pushes theint
value -1 onto the operand stack.
可以看到 这里比较还是根据 float 进行的比较, 那这不就成了套娃了? 那么更下层的计算是怎么处理的呢?, 这才是我们需要关注的"根本原因"
上面拿到的 t1 值为 -1, 经过 fold1 处理之后, 转换为 ifeq 期望的 bool, 值为 false
HotSpotVM 中的 float 比较
此篇幅截图基于 jdk9
为了更友好一些, 我们直接看 byteCodeInterceptor 中的实现逻辑
VMfloatCompare 的实现如下, 是通过 jfloat 的 大于, 等于, 小于 等操作符来控制的
我们这里还需要明确一下 jfloat 是什么, java 中的 float 映射到 c 层面是什么呢?
呵呵 就是 c 中普通的 float 类型
那么 我们来验证一下 c 中的 NaN 呢?
c/c++ 中的 float 比较
测试用例如下
//
// Created by Jerry.X.He on 2021/10/31.
//
int main(int argc, char** argv)
float Nan = 0.0 / 0.0f;
float Max = 1.0 / 0.0f;
float Min = -1.0 / 0.0f;
bool equals01 = Nan == Nan;
bool equals02 = Nan != Nan;
bool equals03 = Max == Max;
bool equals04 = Min == Min;
return 0;
执行结果如下
看到了吧, 到这里 就先到此为止了
再往下, 可能需要参考 浮点数 的相关规范了
完
以上是关于45 Float.NaN == Float.NaN 为 false 是怎么实现的?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 float.NaN != double.NaN 在 C# 中?