java浮点型格式_java浮点类型float和double的主要区别,它们的小数精度范围大小是多少?...

要想理解float和double的取值范围和计算精度,必须先了解小数是如何在计算机中存储的:

举个例子:78.375,是一个正小数。要在计算机中存储这个数,需要把它表示为浮点数的格式,先执行二进制转换:PS:

二进制的小数点和十进制的小数点是不同的。二进制小数点后是2的负幂,十进制是10的负幂。

一 小数的二进制转换(浮点数)

78.375的整数部分:

小数部分:

所以,78.375的二进制形式就是1001110.011

然后,使用二进制科学记数法,有

注意,转换后用二进制科学记数法表示的这个数,有底有指数有小数部分,这个就叫做浮点数

二 浮点数在计算机中的存储

在计算机中,保存这个数使用的是浮点表示法,分为三大部分:

第一部分用来存储符号位(sign),用来区分正负数,这里是0,表示正数

第二部分用来存储指数(exponent),这里的指数是十进制的6

第三部分用来存储小数(fraction),这里的小数部分是001110011

需要注意的是,指数也有正负之分,后面再讲。

如下图所示(图片来自维基百科):

比如float类型是32位,是单精度浮点表示法:

符号位(sign)占用1位,用来表示正负数,

指数位(exponent)占用8位,用来表示指数,

小数位(fraction)占用23位,用来表示小数,不足位数补0。

而double类型是64位,是双精度浮点表示法:

符号位占用1位,指数位占用11位,小数位占用52位。

到这里其实已经可以隐隐看出:

指数位决定了大小范围,因为指数位能表示的数越大则能表示的数越大嘛!

而小数位决定了计算精度,因为小数位能表示的数越大,则能计算的精度越大咯!

(2019/01/30 删掉了之前对精度的不恰当例子,补充下列内容)

补充一点:浮点数的范围和精度的详细解释

先讨论精度。说到精度就离不开“近似值”这个名词,因为浮点格式的“尾数位”是有限的,比如单精度的“尾数位”是23位。

但是,当一个十进制数值转换为二进制科学表达式之后,所得到的尾数位数是有可能很长甚至是无限的!比如十进制数字0.123,用二进制表达时尾数的位数是无限长的,即尾数是无限长的。所以当使用浮点格式来存储这个数字的时候,实际存储的尾数是被截取或执行舍入后的近似值(因为存储尾数的尾数有限),近似值的精确程度就是精度。

用草图简单建了个模,可视化的方式有助于理解。(请把下图当作有分支的数据流图)

单精度格式的尾数位为23,加上隐含的小数点前面的1,则实际上尾数的位数为24位(然而原值的尾数为无限),也就是说,精度p=24,当然这是二进制下的精确位数。转换为十进制,可加个log函数,得7.22

Double精度同理。

近似值与原值是肯定存在差异的,这个差异越小,则精度越高。比如若以double类型来保存0.123,则差异会比用float类型的小。

有两个页面很有帮助,去操作并理解一下很有用。

把实际存储的十六进制浮点数值再精确转换为十进制(在第2个页面可转换),你会发现实际存储的数值是0.12300000339746475(已舍入),或0.12299999594688416(未舍入),显然,执行舍入算法后的误差较小。

为什么会有这种差异呢?原因在于,若没有执行舍入操作,则float将忽略所有后面的那些不能保存的尾数位数,也就是隐藏了(见IEEE-754 Reference Material 的 table 1,我理解为截取),但若是执行了舍入算法操作,误差将会显著降低(更多请参考《深入理解计算机》2.4.4节)

有时间会举个例子详细分析,比如0.123的存储和精度损失, 但时间有限,先这样。

三 指数位的偏移量与无符号表示

需要注意的是指数可能是负数,也有可能是正数,即指数是有符号整数,而有符号整数的计算是比无符号整数麻烦的。所以为了减少不必要的麻烦,在实际存储指数的时候,需要把指数转换成无符号整数。那么怎么转换呢?

注意到float的指数部分是8位,IEEE规定这个指数的取值范围是 -126到+127(详见下文),为了消除负数带来的实际计算上的影响(比如比较大小,加减法等),可以在实际存储的时候,给指数做一个简单的映射,加上一个偏移量,比如float的指数偏移量为127,这样就不会有负数出现了。

比如

指数如果是6,则实际存储的是6+127=133,即把133转换为二进制之后再存储。

指数如果是-3,则实际存储的是-3+127=124,即把124转换为二进制之后再存储。

当我们需要计算实际代表的十进制数的时候,再把指数减去偏移量即可。

对应的double类型,存储的时候指数偏移量是1023。

四 阶码的取值范围

(2018/10/19)

指数位通常译作阶码,这里重点再解释一下阶码的取值范围问题,

为什么阶码的取值范围是-126到+127呢?

分情况讨论即可明白.这里需要用到偏移量,以单精度为例,偏移量 Bias = 127 .当指数位不全是0也不全是1时(规格化的数值),IEEE规定,阶码计算公式为 e-Bias . 此时e最小值是1,则1-127= -126 ; e最大值是254,则254-127=127.可以看到,这种情况下取值范围是-126到127.

当指数位全部是0的时候(非规格化的数值),IEEE规定,阶码的计算公式为1-Bias,即1-127= -126 .

当指数位全部是1的时候(特殊值),IEEE规定这个浮点数可用来表示3个特殊值,分别是正无穷,负无穷,NaN(not a number) . 具体的,小数位不为0的时候表示NaN;小数位为0时,当符号位s=0时表示正无穷,s=1时候表示负无穷.

附图一张,单精度浮点数值的分类. (参考深入理解计算机第2版)

另外,正常来说,在java中,8位单字节byte的取值范围的确是 -128到127 , 但这是整型哦,不是浮点数,注意别混淆了.

浮点数较为繁琐复杂,有较多难点,比如阶码的取值范围,小数位的隐藏的1,以及符号位可表示的正负无穷甚至正负0,都需要分情况讨论和理解.

比如:

若该浮点数是规格化数值,则尾数(由小数位表示)含有隐藏的1;

若该浮点数是非规格化数值,则尾数不含有隐藏的1。

这样能够让非规格化数值和规格化数值的边界值仍然保持连续性。

(最大非规格值过渡到最小规格值,图中是尾数值是7/8平滑过渡到8/8,实际浮点数值是7/512到8/512),这种在不同分类下的转变是平滑的,连续的.

可参考深入理解计算机系统第3版的图2-35,如下:

事实上,这是为了尽可能的表示更多的数值(数轴覆盖),并在多种情况下都能让数值连续(考虑用分数表示).深入理解计算机系统第2版或第3版中的2.4节较好的说明了这一点.具体不再赘述.

五 最后

所以用float类型来保存十进制小数78.375的话,需要先转换成浮点数,得到符号位和指数和小数部分。这个例子前面已经分析过,所以:

符号位是0,

指数位是6+127=133,二进制表示为10 000 101,

小数部分是001110011,不足部分请自动补0。

连起来用float表示,加粗部分是指数位,最左边是符号位0,代表正数:

0 10000101 001110011 00000 00000 0000

如果用double来保存。。。自己计算吧,太多0了。

因为刚好我正在思考这个float和double类型的取值范围究竟是怎么算出来的,网上各种blog抄来抄去,解释不尽如人意,最终还是得求助于书本。

参考资料:《80x86汇编语言与计算机体系结构》 第一章 p14页 ,用了里面的例子,省时省力。一本比较古老的书了,然而用处还是很大的。

IEEE 754 维基百科通常会有一些比较形象的图片解释

深入理解计算机系统第3版

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值