IEEE 754规范

IEEE

IEEE(Institute of Electrical and Electronics Engineers,简称为IEEE)中文称之为电气电子工程师学会,基本也就叫IEEE,读作i triple eIEEE是一个建立与1963年1月1日的国际性电子技术和电子工程师协会,是世界最大的专业技术组织之一。目前IEEE在工业界所定义的标准有着极大的影响。

IEEE-754 规范即是一个比较有影响的标准。

在六、七十年代,各家计算机公司的各个型号的计算机,有着千差万别的浮点数表示,却没有一个业界通用的标准。这给数据交换、计算机协同工作造成了极大不便。IEEE的浮点数专业小组于七十年代末期开始酝酿浮点数的标准。在1980年,英特尔公司就推出了单片的8087浮点数协处理器,其浮点数表示法及定义的运算具有足够的合理性、先进性,被IEEE采用作为浮点数的标准,于1985年发布。而在此前,这一标准的内容已在八十年代初期被各计算机公司广泛采用,成了事实上的业界工业标准。加州大学伯克利分校的数值计算与计算机科学教授威廉·卡韩被誉为“浮点数之父”。

IEEE-754

首先看两个数字的表示方式:

以上均是以10为底的指数表示方式。

同理是否可以用2为底的指数形式来表示:

所以 IEEE-754的实现方式其实就是以2为底指数来表达一个数字。

image-20200524231441051

整个数字表达分为三部分,有三部分来确定一个数值是多少。

  • sign:符号位 0为正,1为负
  • exponent:指数数字
  • fraction:分数或小数

当然这里涉及到个位数所占数量的多少,IEEE-754 针对不同位数有相应的规范。

精度 符号位(S) 指数位(E) 分数位(M) 总位数
单精度 1 8 23 32
双精度 1 11 52 64

IEEE的表达式如下:

演示一个例子:

                Float f = 0.025F;
        System.out.println(Integer.toBinaryString(Float.floatToIntBits(f)));
                // 0011 1100 1100 1100 1100 1100 1100 1101

将二进制以单精度进行划分后(1+8+23):

【0】【011 1100 1】【100 1100 1100 1100 1100 1101】
S = 0
E = 121 - 127 (IEEE754以无符号存储,单精度每个都加了127,称之为偏移量Bias)
M = 0.6000000238418579 + 1

这里需要考虑到一个精度的问题,因为单精度在设计时,就限定了指数范围以及分数的位数,所以当表达一些很小的数字是就会面临精度丢失的问题。所以设计的时候还需要考虑规约(Normalized Number) 和非规约 (Denormalized Number)两种场景,规约形式默认有一位隐形有效数字1,比如以下一个数字的两种表达:

从而非规约形式能表达的更小的数据。

精度的问题

1、在Java语言中,对于Float使用了单精度的存储,Double场景使用了双精度存储。

System.out.println(0.1d + 0.2d);
//输出 0.30000000000000004

来看下0.1和0.2的表示方式

0.1的二进制转换
0.1*2=0.2  >0
0.2*2=0.4  >0
0.4*2=0.8  >0
0.8*2=1.6  >1
0.6*2=1.2  >1
0.2*2=0.4  >0
0.4*2=0.8  >0
0.8*2=1.6  >1
0.6*2=1.2  >1
0.2*2=0.4  >0
0.4*2=0.8  >0
0.8*2=1.6  >1
0.6*2=1.2  >1
...
无限循环 0.0001100110011...
0.2的二进制转换
0.2*2=0.4  >0
0.4*2=0.8  >0
0.8*2=1.6  >1
0.6*2=1.2  >1
0.2*2=0.4  >0
0.4*2=0.8  >0
0.8*2=1.6  >1
0.6*2=1.2  >1
0.2*2=0.4  >0
0.4*2=0.8  >0
0.8*2=1.6  >1
0.6*2=1.2  >1
...
无限循环 0.001100110011...

十进制0.1和0.2用二进制表达式会展示成无限循环小数,类似10进制里的1/3。因为存储的位数是有限的,那么势必会丢失精度。

Java上使用BigDecimal来计算能避免浮点数计算丢失精度的问题,BigDecimal并没有使用二进制的浮点数来进行计算,而是将浮点数的整数部分和复数部分单独计算,保证了计算时的精度问题(不要使用)。

//正确的姿势:
System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2")));
//输出:0.3

//错误的姿势:
System.out.println(new BigDecimal(0.1).add(new BigDecimal(0.2)));    
//输出:0.3000000000000000166533453693773481063544750213623046875

2、Javascript 场景中仅用了Number一个数值类型,采用的是IEEE-754的双精度浮点数编码,同样面临精度问题

因为Javascript 仅适用Number类型存储数字类型,那么对于通常意义上的Long类型存储也存在精度丢失的问题,正因为IEEE754双精度限制了分数位52位,即52位有效位,如果二进制超过52位的数据将存在精度丢失,即最大安全数字为

参考:

电气电子工程师学会

IEEE-754 在线计数器

IEEE754揭秘浮点数

你应该知道的浮点数基础知识

你可能感兴趣的:(IEEE 754规范)