为了账号安全,请及时绑定邮箱和手机立即绑定

【Java核心技术卷】深入理解Java近似数(近似存储)与有效数字、精确度、最大有效位数的概念

标签:
Java

近似数与有效数字的概念

近似数是由于近似存储(内存空间有限)引起的。
近似数是由四舍五入得来的数,如10/3中的3是一个准确数,而3.3是它精确到十分位的近似数。6.67是20/3精确到百分位的近似数。
而有效数字是对于近似数从左边第一个不是0的数字起,到精确到的数位止,所有的数字都是这个近似数的有效数字,如3.3有两个有效数字3,3 , 6.67有三个有效数字6,6,7。
浮点类型仅是数学中小数的一种模拟,不能将其与数学中的小数运算等同。计算机中的浮点类型只是近似的存储。

精确度的确定

近似数的精确度的确定有两种形式,一是精确到哪一位,另一种是保留几位有效数字。
计算机中描述精确度通常使用有效位数

近似数精确到那一位是由所得的近似数的最后一位有效数字在该数中所处的位置决定的,
如0.548,“8”在千分位,则0.548精确到千分位或精确到0.001。
4.80数字“0”在百分位上,则4.80精确到百分位或精确到0.01。
而对于一个有单位或用科学记数法表示的近似数,其精确度的确度经常是掌握的一个难点内容,如2.4万,它实际上是24000,数字“4”在千位上,则2.4万精确到千位。 若直接判断有困难,可以先化为57300,数字“3”在百位上,则 精确到百位,所以对于带有单位或用科学记数法表示的近似数,确定精确度是与它的单位和 有关

对于一个近似数的有效数字的确定,必须按定义进行。如0.03086,有4个有效数字3、0、8、6,而数字“3”前面的两个0不是有效数字。6.090有4个有效数字6、0、9、0,要注意数字之间和后边的“0”都是有效数字。对于带有单位和用科学记数法表示的近似数,其有效数字与单位和 无关,如3.80万,有三个有效数字3、8、0; 有三个有效数字6、9、5。

相似的近似数的区别

1、如近似数4.8与4.80的区别
4.8精确到十分位,有2个有效数字,它可以由4.75和4.84之间的数近似得到的,4.80精确到百分位,三个有效数字可以由4.795和4.804之间的数近似得到的。
2、5.6×100 与560的区别
5.6精确到十位,有2个有效数字,560精确到个位有三个有效数字。
3、45300与 453 区别
45300精确到个位,有5个有效数字, 精确到百位,有3个有效数字。

最大有效位数

由于计算机的物理存储位数有限,所以近似存储能够实际存储的十进制的最大有效位数也是有限的。

IEEE745浮点数格式
无论是单精度还是双精度在存储中都分为三个部分:

  1. 符号位(Sign) : 0代表正,1代表为负
  2. 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储
  3. 尾数部分(Mantissa):尾数部分

1.双精度格式
双精度的存储方式为:
​​​​image

IEEE双精度浮点格式共64位,占2个连续32位字,包含三个构成字段:52位的小数f,11位的偏置指数e,1位的符号位s。
将这2个连续的32位字整体作为一个64位的字,进行重新编号,其中0:51位包含52位的小数f;52:62位包含11位的偏置指数e;而最高位,第63位包含符号位s。如图2所示。
image

f[31:0]存放小数f的低32位,其中第0位存放整个小数f的最低有效位LSB,第31位存放小数f的低32位的最高有效位MSB。
在另外的32位的字里,第0 到19位,即f[51:32],存放小数f的最高的20位,其中第0位存放这20位最高有效数中的最低有效位LSB,第19位存放整个小数f的最高有效位MSB。

第20到30位,即e[52:62],存放11位的偏置指数e,其中第20位存放偏置指数的最低有效位LSB,第30位存放最高有效位MSB。最高位,第31位存放符号位s。

在Intel x86结构计算机中,数据存放采用小端法(little endian),故较低地址的32位的字中存放小数f的f[31:0]位。而在SPARC结构计算机中,因其数据存放采用大端法(big endian),故较高地址的32位字中存放小数f的f[31:0]位。

2.单精度格式

其中float的存储方式如下图所示:
image

IEEE单精度浮点格式共32位,包含三个构成字段:23位小数f,8位偏置指数e,1位符号s。将这些字段连续存放在一个32位字里,并对其进行编码。其中0:22位包含23位的小数f; 23:30位包含8位指数e;第31位包含符号s。如图1所示。
image

(图 单精度存储格式)

一般地,32位字的第0位存放小数f的最低有效位LSB(the least significant bit),第22位存放小数f的最高有效位MSB(the most significant bit);第23位存放偏置指数的最低有效位LSB,第30位存放偏置指数的最高有效位MSB;最高位,第31位存放符号s。

3.浮点数的分类

根据偏置指数e的值,被编码的浮点数可分成三种类型。
(1)规格化数 v = s×1.f×2E E = e - Bias,其中Bias=2k-1-1,k为指数位数
当有效数字M在范围1≤M<2中且指数e的位模式ek-1…e1e0既不全是0也不全是1时,浮点格式所表示的数都属于规格化数。这种情况中小数f(0≤f<1 ) 的二进制表示为0. fn-1…f1f0。有效数字M=1+f,即M=1. fn-1…f1f0 (其中小数点左侧的数值位称为前导有效位) 。我们总是能调整指数E,使得有效数字M在范围1≤M<2中,这样有效数字的前导有效位总是1,因此该位不需显示表示出来,只需通过指数隐式给出。

需要特别指出的是指数E要加上一个偏置值Bias,转换成无符号的偏置指数e,也就是说指数E要以移码的形式在存放计算机中。且e、E和Bias三者的对应关系为e=E+Bias,其中Bias=2k-1-1,k为指数位数。

比如
JavaScript 能存储的最大数为:
1 x (2^53 - 1) x 2^971 = 1.7976931348623157e+308;//971=1023–52
这个值正是 Number.MAX_VALUE

(2)非规格化数 v = s×0.f×2E E=1-Bais //1-1023
当指数e的位模式ek-1…e1e0全为零(即e=0)时,浮点格式所表示的数是非规格化数。这种情况下,E=1-Bais,有效数字M=f=0. fn-1…f1f0 ,有效数字的前导有效位为0。
非规格化数的引入有两个目的。其一是它提供了一种表示数值0的方法,其二是它可用来表示那些非常接近于0.0的数。

比如
JavaScript 能存储的最小数为:
1 x 1 x 2^(-1074) = 5e-324; //-1074 = (-1022) + (-52)
这个值正是 Number.MIN_VALUE

(3)特殊数
当指数e的位模式ek-1…e1e0全为1时,小数f的位模式fn-1…f1f0全为0(即f=0)时,该浮点格式所表示的值表示无穷,s=0 时是+∞,s=1时是-∞。
当指数e的位模式ek-1…e1e0全为1时,小数f的位模式fn-1…f1f0不为0(fn-1、…、f1、f0、至少有一个非零即f≠0)时,该浮点格式所表示的值被称为NaN(Not a Number)。比如当计算 或∞-∞时用作返回值,或者用于表示未初始化的数据。

IEEE745双精度浮点数格式小结:

1.指数e的位模式ek-1…e1e0全为1,即e=211-1时, 特殊数

1.1 正无穷大 +Infinit (实际上是最大值)
s=0, 小数f的位模式fn-1…f1f0全为0(即f=0)时

1.2 负无穷大 +Infinit(实际上是最小值)
s=1, 小数f的位模式fn-1…f1f0全为0(即f=0)时

1.3 NaN(Not a Number)
小数f的位模式fn-1…f1f0不为0(fn-1、…、f1、f0、至少有一个非零即f≠0)时

2.指数e的位模式ek-1…e1e0全为零,即e=0时,非规格化数
这种情况下,E=1-Bais,有效数字M=f=0. fn-1…f1f0 ,有效数字的前导有效位为0
2.1 +0: e=0, f=0, s=0
s=0, 小数f的位模式fn-1…f1f0全为0(即f=0)时
2.2 -0: e=0, f=0, s=1
s=1, 小数f的位模式fn-1…f1f0全为0(即f=0)时
2.3 Number.MIN_VALUE : e=0, f≠0,f=1 x 2^(-52)
1 x 1 x 2^(-1074) = 5e-324; //-1074 = (-1022) + (-52)
2.4 非常接近于0.0的数: e=0, f≠0
s x 0.f x 2^(-1022)

3.指数e的位模式ek-1…e1e0既不全是0也不全是1时, 规格化数

3.1 Number.MAX_VALUE
1 x (2^53 - 1) x 2^971 = 1.7976931348623157e+308;//971=1023–52
3.2 s×1.f×2E E = e - Bias,其中Bias=2k-1-1,k为指数位数
E = 1023 ~ -1022

下溢(underflow):小数点1074位后由于超出了Number表示的下限范围,低位将无法存储而丢失,。是当运算结果无限接近于零并比JavaScript能表示的最小值还小的时候发生的一种情形。MinValue = s×5×2e-1074
溢出(overflow):当数字运算结果超出了JavaScript所能表示的数字上限(溢出),结果为一个无穷大(infinity)值。上限指二进制整数部分为1024位。
低位丢失:能够存储的最大二进制有效位数为53位,53位后的二进制无法存储而丢失。

小数的精度丢失(低位丢失)
十进制 0.1 的二进制为 0.0 0011 0011 0011 … (循环 0011)
十进制 0.2 的二进制为 0.0011 0011 0011 … (循环 0011)

0.1 + 0.2 相加可表示为:
e = -4; m = 1.10011001100…1100(52 位 尾数,1位前导有效位)
+ e = -3; m = 1.10011001100…1100(52 位)


e = -3; m = 0.11001100110…0110
+ e = -3; m = 1.10011001100…1100


e = -3; m = 10.01100110011…001 (52 位 尾数,1位前导有效位)

= 0.01001100110011…001
= 0.30000000000000004(十进制)
根据上面的演算,还可以得出一个结论:当十进制小数的二进制表示的有限数字不超过 53 位时,在 JavaScript 里是可以精确存储的。

大整数的精度丢失(低位丢失)

9999999999999999 == 10000000000000000; // true
很明显,16 个 9 还远远小于 308 个 10. 这个问题与 MAX_VALUE 没什么关系,还得归属到尾数 f 只有 52 位上来。
可以用代码来描述:
var x = 1; // 为了减少运算量,初始值可以设大一点,比如 Math.pow(2, 53) - 10
while(x != x + 1) x++;
// x = 9007199254740992 即 2^53
也就是说,当 x 小于等于 2^53 时,可以确保 x 的精度不会丢失。当 x 大于 2^53 时,x 的精度有可能会丢失。比如:
x 为 2^53 + 1 时,其二进制表示为:
10000000000…001 (中间共有 52 个 0)

用双精度浮点数存储时:
e = 1; m = 10000…00(共 52 个 0,其中 1 是 hidden bit)

显然,这和 2^53 的存储是一样的。
按照上面的思路可以推出,对于 2^53 + 2, 其二进制为 100000…0010(中间 51 个 0),也是可以精确存储的。
规律:当 x 大于 2^53 且二进制有效位数大于 53 位时,就会存在精度丢失。这和小数的精度丢失本质上是一样的。

PS:整型的0除以0与浮点型的0.0除以0.0结果是不一样的,一个是结果异常,一个结果是NaN

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消