Java浮点数机制及所存在的问题

时间:2022-07-23
本文章向大家介绍Java浮点数机制及所存在的问题,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Java中浮点数的机制,IEEE 754规则,以及为什么在java中0.1+0.2!=0.3

Java浮点数机制及所存在的问题

0. 背景

总所周知,即使是小朋友也知道0.1+0.2 = 0.3肯定是正确的,但是在Java中,如果输入 0.1+0.2 == 0.3,返回的却是false 在Java中,如果你动手尝试输入 0.1+0.2,可以看到返回的值是0.30000000000000004,至于为什么会发生这样的事情,这便是后面要探讨的了——Java浮点数机制。

1. Java浮点数机制

通过查阅资料可以发现,现在很多主流的语言对浮点数的实现都是采用的IEEE 754,其中这些语言中也包含Java,要了解Java的浮点数机制,也就得了解IEEE 754是如何定义浮点数的 IEEE 浮点数标准是从逻辑上用三元组{S,E,M}来表示一个数 V 的,即 V=(-1)S×M×2^E

上图分别表示了不同精度的浮点数 其中: 符号位 s(Sign)决定数是正数(s=0)还是负数(s=1),而对于数值 0 的符号位解释则作为特殊情况处理。 有效数字位 M(Significand)是二进制小数,它的取值范围为 1~2-ε,或者为 0~1-ε。它也被称为尾数位(Mantissa)、系数位(Coefficient),甚至还被称作小数指数位 E(Exponent)是 2 的幂(可能是负数),它的作用是对浮点数加权。

类型(type)

符号位(sign)

指数位(biased exponent)

有效数位(normalised mantisa)

偏值(bias)

单精度(Float)

1(31st bit)

8(30-23)

23(22-0)

127

双精度(Double

1(63st bit)

11(62-52)

52(51-0)

1023

下面用几个例子来做示范

// 原始值
85.125
// 转换成二进制形式
85 = 1010101
0.125 = 001
85.125 = 1010101.001
       =1.010101001 x 2^6 
// 正数
sign = 0 

// 在单精度中的表现形式
// 指数位,因为需要用8位指数来表示正负两种情况,所以这里需要用6+偏值
biased exponent = 127+6 = 133
133 = 10000101
Normalised mantisa = 010101001  //后面将会自动补0到23位长度

// 所以在IEEE 754中该数的单精度的表示
0 10000101 01010100100000000000000
// 转换为十六进制 
42AA4000

// 在双精度中的表现形式
biased exponent = 1023+6=1029
1029 = 10000000101
Normalised mantisa = 010101001  //后面将会自动补0到52位长度

// 所以在IEEE 754中该数的双精度的表示
0 10000000101 0101010010000000000000000000000000000000000000000000
// 转换为十六进制 
4055480000000000

3. 为什么0.1+0.2 != 0.3

知道了在Java中的浮点数运行机制后,再来解决这个问题就很好办了

// 第一步求出0.1的二进制形式
0.1 x 2 = 0.2   0
0.2 x 2 = 0.4   0
0.4 x 2 = 0.8   0
0.8 x 2 = 1.6   1
0.6 x 2 = 1.2   1
0.2 x 2 = 0.4   0
.....
// 所以最后计算出来0.1的二进制表现形式为一个无限循环小数
0.1 = 0.000110011001100.... x 2^0

// 使用IEEE754 来表示
1.10011 ... x 2^(-4)
0 1.1001100110011001100110 01111011
// 0.2 的最终表现形,指数位+1即可
0 1.1001100110011001100110 01111100
// 所以最后的0.3
0 1.0011001100110011001100 01111101
及
0 01111101 00110011001100110011001
// 再将次数转成二进制是就成了0.30000000000000004
所以0.1+0.2在Java中并不等于0.3