浮点数

Posted shizunatsu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浮点数相关的知识,希望对你有一定的参考价值。

1.浮点数是什么

在计算机中表达实数的方式有定点数和浮点数。定点数的小数点位置固定,不能移动,浮点数的小数点位置可以移动。

所以一个浮点数可以有多种表达方式,如101.101可以表示成1.01101*2^2、1011.01*2^-1。

Java的float和double采用了IEEE 754标准中所定义的单精度 32 位浮点数和双精度 64 位浮点数的格式,用二进制的科学计数法来表示浮点数。

 

2.浮点数的表示

Java语言的浮点数有两种表示形式

十进制表示:123.0,23.45

科学计数法表示:8.3e2,9.2E-3

   1> e是exponent的首字母,指数的意思,表示10的几次方,大小写均可

   2> e或E之前必须有数,注意指数有正有负

        3> 只有浮点类型的数值才可以使用科学计数法形式表示。例如,51200是一个int类型的值,但512E2则是浮点类型的值

 

3.浮点数的内存

float在内存中占4个字节32位,内存区域分为三个部分。 

技术图片

尾数位:0-22位,共23位

指数位(阶码位):23-30位,共8位

符号位:最高位,0表示正数1表示负数

 

double在内存中占8个字节64位。最高位为符号位,接下来11位为指数位,最后52位为尾数位

 

根据IEEE754标准规定

对于阶码e ,如果不是0或者255, 就需要减去偏差值,对于float 是127 ,double是1023。减去偏差值的数值才是真正的指数
对于尾数M ,如果阶码不是0或者255,尾数的小数点左侧有一个默认的 1

 

4.浮点数的进制转换

十进制小数转二进制小数,整数部分和小数部分需要分开处理

  • 整数部分:除以2直到商为0,反序取余

  • 小数部分:乘以2,取结果的整数部分,再用结果的小数部分乘以2,如此循环下去,直到小数部分为0。然后将整数顺序排列。

        如果小数部分永远不为0,则按规定进行取舍。

(因为可能出现永远不为0的情况,所以就注定了有些十进制小数无法用二进制小数精确表示)

来看一个具体例子


float f= 10.8125f;

10.8125整数部分转换为二进制是1010,小数部分转换成二进制是1101

所以10.8125对应的二进制小数位1010.1101,规范写法为1.0101101*10^3

底数去掉1和小数点为0101101,指数为3+127=130,二进制为10000010,符号位为0

即10.8125的二进制为

0100 0001 0010 1101 0000 0000 0000 0000

用程序看一下10.8125的二进制表示

System.out.println(Integer.toBinaryString(Float.floatToIntBits(10.8125f)));

结果为

1000001001011010000000000000000

前面补0为

0100 0001 0010 1101 0000 0000 0000 0000

跟我们计算的一样

 

5.浮点数不精确

浮点数在计算机中用以近似表示任意某个实数。近似,是什么意思呢?就是用一个近似值去表示一个数。

浮点数并不一定等于小数。 

 

如float f = 0.1f ;计算机底层是无法用二进制去精确表示0.1的,它只会有一个无限接近于0.1的二进制小数

0.1f 在计算机中存储的二进制是  0011 1101 1100 1100 1100 1100 1100 1101,相当于10进制的0.100000001490116119384765

在计算机中,用0.100000001490116119384765这个近似值去表示小数0.1

打印出来也是0.1

 

那为什么0.1无法精确保存打印出来的却还是0.1?

因为浮点数就是用这个近似值去表示0.1的

只要你的二进制存储是这个,那么,Java会认为你就是浮点数0.1

尽管按照十进制的算法,这个数字并不等于0.1而是趋近于0.1.

但这也恰好印证了,浮点数不等于小数,而是以近似的方式去表示实数的

 

还有一种情况

float f = 5.2345556f;
System.out.println(f);//5.2345557

把5.2345556f赋值给一个float类型变量,接着输出这个变量时看到这个变量的值已经发生了改变。

 

为什么打印出来数字会发生变化?

1.计算机无法用二进制小数精确表示一个十进制小数

float f =  5.2345556f; 

在内存中的二进制为0100 0000 1010 0111 1000 0001 0111 1011,转换为十进制为5.234555721282958984375,保留8位精度为5.2345557

2.浮点数的取值范围

浮点数是有取值范围的,float的取值范围是-3.4*10^38 ~ 3.4*10^38

那么,它能够表达数轴上在这个范围内的所有数吗?显然是不能的,首先无限小数就无法表示。

如果把浮点数能够表示的所有数在数轴上一一列出来,我们会发现这不是一个完整的线段,而是中间带有间隔

 

参考:https://my.oschina.net/jasonli0102/blog/3013198

 

 6.当我们定义float f = 0.1时, 计算机是如何进行保存的?

首先分配4个字节去存储,然后把符号位、尾数位、指数位分别填入

 

这里我们看一下尾数位

将十进制的0.1 转换为二进制

小数部分乘以2    0.1*2 = 0.2;  0.2*2=0.4;  0.4*2=0.8;  0.8*2=1.6;  0.6*2=1.2;(这里小数部分又得到了最开始的2,这意味着会无限循环下去)

取整数部分                          0      0                       0                        1                     1         (接下来取到的整数是  0 0 1 1的循环)

 

所以0.1对应的二进制小数为0.0 0011 0011……(0011循环)

将其转换为浮点数的规范表达形式, 1.1001100110011……*2^-4

 

float的尾数除去默认的小数点前面的1,有23位。所以将小数点后面的值依次填入尾数,最后一位按照规定进行取舍,剩下的直接舍弃

忽略最后一位的取舍的话,尾数应该是 100 1100 1100 1100 1100 1100

查看一下0.1的二进制表示

System.out.println(Integer.toBinaryString(Float.floatToIntBits(0.1f)));//111101110011001100110011001101

即尾数应该是 100 1100 1100 1100 1100 1101,除了最后一位,其他跟我们预想的一样

 

   

7.阶码为何使用偏差值,float偏差值为何是127?

float的偏差值是127(2^7-1),double的偏差值是1023(2^10-1)

阶码使用偏移量是为了简化运算。

指数是有正有负的。如果把指数位的最高位作为符号位的话,一个浮点数中就会有两个符号位了,那浮点数之间的比较和运算想必会困难许多。

使用了偏移量之后,用无符号整数既可以表示正指数,又可以表示负指数

 

为什么偏移量是127?

8位可以表示 0000 0000 ~ 1111 1111 即0~255共256个值

规范规定0000 0000与1111 1111用作特殊情况,所以除去0和255,阶码能表示1~254共254个值

为了平衡正负指数,选取了1~254中间的值127。

 

8.浮点数取值范围

当尾数位全部为1时,底数取得最大值,接近于2。当尾数位全部置0时,底数取得最小值,为1。故底数的取值范围为 1~2。
指数位的取值范围为1~254,减去偏差值为 -126~127,故指数的取值范围为-126~127。
因此float型的取值范围为:
-2*2^127 ~ -1*2^(-126) 与 1*2^(-126) ~ 2*2^127
转化得:
-3.4*10^38 ~ -1.2*10^(-38) 与 1.2*10^(-38) ~ 3.4*10^38

 


9.浮点数的大小比较

在二进制中,是通过符号位、指数位、尾数位分别比较得到结果的

有一个不相等则不相等

注意指数位的比较,比较的是减去偏移量之后的数

 

 

10.浮点运算

浮点计算是指浮点数参与的运算,这种运算通常伴随着因为无法精确表示而进行的近似或舍入

完成浮点加减运算的操作过程大体分为四步:
1. 0 操作数的检查;
2. 比较阶码大小并完成对阶;
3. 尾数进行加或减运算;
4. 结果规格化并进行舍入处理。
 

对于0.1+0.2,为什么不精确?

因为这种情况,至少有一个就不能精确表示,然后运算的时候是底层二进制进行运算。

 

以上是关于浮点数的主要内容,如果未能解决你的问题,请参考以下文章

原生JavaScript判断是否为邮箱危险字符验证长度验证网址验证小数整数浮点数等常用的验证

unity 优化整理

unity 优化整理

JavaScript 有用的代码片段和 trick

js实现浮点数保留两位小数代码

在 C++ 中决定普通浮点数的代码? [复制]