前言

  Go语言之父Rob Pike大神曾吐槽:不能掌握正则表达式或浮点数就不配当码农!

  You should not be permitted to write production code if you do not have an journeyman license in regular expressions or floating point math.

  此前使用Java写Spark SQL业务时,也有遇到浮点数比较问题即x>70的记录行居然出现了70的记录,尽管SQL做了类型转换再比较也无济于事....

  因此了解浮点数是很有必要的哟~~

什么是浮点数

  电气和电子工程师协会IEEE对于计算机浮点数的存储、运算、表示等推出了IEEE754标准!

  标准中规定:

    float32位单精度浮点数在机器中表示用 1 位表示数字的符号,用 8 位表示指数,用 23 位表示尾数。

    double64位双精度浮点数,用 1 位表示符号,用 11 位表示指数,52 位表示尾数。

    其中指数域也称为阶码。浮点数存储字节定义如图:

         

浮点数正规化

  尾数不为0时,尾数域的最高有效位为1,这称为规格化。否则,以修改阶码同时左右移动小数点位置的办法,使其成为规格化数的形式。

  浮点数x真值表示:

  x=(−1)S×(1.M)×2e

  float:    e=E−127

  double:     e=E−1023 

  • S  符号位    0表示正 1表示负
  • e  指数位    阶码E减去移码
  • M 尾数位    二进制形式移码

移码

  移码是真值补码的符号位取反,一般用作浮点数的阶码,目的是便于浮点数运算时的对阶操作。

  对于定点整数,计算机一般采用补码的来存储。

  正整数的符号位为0,反码和补码等同于原码。

  负整数符号位都为1,原码,反码和补码的表示都不相同,由负数原码表示法变成反码和补码有如下规则:
  (1)原码符号位为1不变,整数的每一位二进制数位求反得反码;
  (2)反码符号位为1不变,反码数值位最低位加1得补码。

   比如,以一个字节来表示-3,那么[−3]原=10000011 [−3]反=11111100 [−3] 补=11111101  [−3]移=01111101

  

举个栗子

3.14的单精度浮点数表示

首先将3.14转成二进制:

整数部分3的二进制是11

小数部分0.14的二进制是:0.0010001111010111000010[10001111.....](方括号中表示小数点后第23位及之后)

这样,3.14的二进制代码就是:11.0010001111010111000010[10001111....]×20

那么用正规化表示就是:1.10010001111010111000010[10001111....]×21

方括号表示的超出23位之后的二进制部分,由于单精度浮点数尾数只有23位,所以需要舍入(舍入方法见后)

由于第24位为1,且之后 不全为 0,所以需要向第23位进1完成上舍入:1.10010001111010111000011×21

而其指数1,需要加上移码127,即128,也就是1000 0000

它又是正数,所以符号为0

综上所述,3.14的单精度浮点数表示为:
0 1000-0000 1001-0001-1110-1011-1000-011

S符号位    

e指数位 1000-0000

M尾数位  1001-0001-1110-1011-1000-011

十六进制代码为:0x4048F5C3

误差

  通过栗子可知,3.14的单精度浮点数表示是0 1000-0000 1001-0001-1110-1011-1000-011。现在我们来还原,看看它的误差:

  指数是128,那么还原回去(减去移码),实际指数就是1

  尾数还原也就是:10010001111010111000011,所以正规化形式是:1.10010001111010111000011×21

  也就是11.0010001111010111000011

  利用二进制转十进制,可得它对应的十进制数是:3.1400001049041748046875  不等于3.14

  这就是为什么浮点数运算结果在业务代码中总是不可确切预期的原因!!!!

机器ε

  机器ε表示1与大于1的最小浮点数之差。例如双精度表示1和表示大于1的最小浮点数

  

  

  双精度浮点数的机器ε = 2-52 ≈ 2.220446049250313e-16

  同理,单精度的机器ε = 2-23 ≈ 1.1920928955078125e-7

  在舍入规则中,相对舍入误差不能大于机器ε的一半。

非正规化

    单精度浮点数为例

  (1)0的表示

    对于阶码为0或255的情况,IEEE754标准有特别的规定:

    如果 阶码E=0并且尾数M是0,则这个数的真值为±0(正负号和数符位有关)。

    +0的机器码为:0 00000000 000 0000 0000 0000 0000 0000

    -0的机器码为:1 00000000 000 0000 0000 0000 0000 0000

    需要注意一点,浮点数不能精确表示0,而是以很小的数来近似表示0。因为浮点数的真值等于

    x=(−1)S×(1.M)×2e 

    e=E−127

    那么

    +0的机器码真值为  1.0×2−127 

     -0机器码真值为  −1.0×2−127

  (2)无穷的表示
    如果阶码E=255 并且尾数M全是0,则这个数的真值为±∞(同样和符号位有关)。

    因此

    +∞的机器码为:0 11111111 000 0000 0000 0000 0000 0000

    -∞的机器吗为:1 11111111 000 0000 0000 0000 0000 0000

  (3)NaN(Not a Number)
    如果 E = 255 并且 M 不全是0,则这不是一个数(NaN)。

舍入规则

  以23位尾数位的单精度浮点数为例,舍入时需要重点参考第24位

  若第24位为1,且第24位之后全部为0。此时就要使第23位为0:若第23位本来就是0则不管,若第23位为1,则第24位就要向第23位进一位,这样第23位就可以为0

  若第24位为1,且第24位之后不全为0,则第24位就要向第23位进一完成上舍入。

  若第24位为0,此时直接舍去不进位,称为下舍入。

再来个栗子

JavaScript console 双精度浮点数

>>9.4 - 9 - 0.4 === 0
<<false
>>(9.4-9-0.4).toFixed(20)
<<"0.00000000000000033307"

9.4-9-0.4不严格等于0,其运算结果误差。

因为按照上面的浮点数知识可知

9.4在机器内被表示为:9.4+0.2×2-49

0.4被表示为:0.4+0.1×2-52

当9.4-9时(因为9是整数是可以精确存储的)得0.4+0.2×2-49,再减去0.4+0.1×2-52得3×2-53,约等于"0.00000000000000033307"。

详细解释:

9的二进制是1001,而0.4的二进制是0.0110-0110-0110-……无限循环的。从而9.4的二进制是1001.0110-0110……,正规化以后就变成 1.001-0110-0110-……×2^3

因为双精度浮点数是52位尾数,所以小数部分保留0.001-0110-0110-……-0110-0 [110-0110-0110-……]。即001后跟12个0110循环节,然后第52位是0,中括号表示从

第53位起开始舍弃的部分。根据我提到的舍入规则,第53位1且后面不全为0,要向第52位完成上舍入,所以小数部分就变成 0.001-0110-0110-……-0110-1。至此我们

可以看到,这个数较之9.4,由于小数部分第52位由0变为1,所以多加了2-52,但是因为从小数部分第53位开始舍弃了,舍弃部分是 0.1100-1100-…×2-52 = 0.8×2-52

所以我们多加了2-52,但是少了0.8×2-52,这就意味着,但考虑尾数部分,这个数比9.4多了 2-52 - 0.8×2-52 = 0.2×2-52,别忘记之前还有一个2^3,所以整

体多了0.2×2-52×2^= 0.2×2-49

这就是为什么9.4在机器内被表示为:9.4+0.2×2-49

同理,0.4在机器内被表示为:0.4+0.1×2-52

IEEE754浮点数的更多相关文章

  1. 震惊!计算机连0.3+0.6都算不对?浅谈IEEE754浮点数算数标准

    >>> 0.3+0.6 0.8999999999999999 >>> 1-0.9 0.09999999999999998 >>> 0.1+0.1+ ...

  2. 把一个IEEE754浮点数转换为IBM370浮点数的C#代码

    把一个IEEE754浮点数转换为IBM370浮点数的C#代码. 在这个网页上有古老的IBM370浮点格式的说明. // http://en.wikipedia.org/wiki/IBM_Floatin ...

  3. IEEE754 浮点数

    IEEE754 浮点数 1.阅读IEEE754浮点数 A,阶码是用移码表示的,这里会有一个127的偏移量,它的127相当于0,小于127时为负,大于127时为正,比如:10000001表示指数为129 ...

  4. IEEE754浮点数表示法

    IEEE二进制浮点数算术标准(ANSI/IEEE Std 754-1985)是一套规定如何用二进制表示浮点数的标准.就像"补码规则"建立了二进制位和正负数的一一对应关系一样,IEE ...

  5. IEEE Floating Point Standard (IEEE754浮点数表示法标准)

    浮点数与定点数表示法是我们在计算机中常用的表示方法 所以必须要弄懂原理,特别是在FPGA里面,由于FPGA不能像在MCU一样直接用乘除法. 定点数 首先说一下简单的定点数,定点数是克服整数表示法不能表 ...

  6. matlab中实现 IEEE754浮点数 与 一般十进制数之间 互相转换的方法

    ------------恢复内容开始------------ %2020/12/2 11:42:31clcformat long % IEEE754 to deca = '40800000'a = d ...

  7. IEEE754浮点数的转换

    将十进制数转换为单精度浮点数 如何将十进制数转换为单精度浮点数参考 首先要知道 IEEE浮点标准:V=(-1)^s * M * 2^E 1.符号(sign)s决定这个数是负数(s=1)还是正数,0(s ...

  8. IEEE754浮点数的表示方法

    https://blog.csdn.net/K346K346/article/details/50487127

  9. 作业:IEEE754浮点数

    人工转换: 5.75转换成二进制:101.11右移2位,补0:1.0111000000000000000000000000000000000000000000000000.10000000001 16 ...

随机推荐

  1. onenote 每天输入网络密码

    1.问题:只局限 内网 笔记本的弹出输入远程内网服务器用户名密码的情况,每次重启电脑后又会要求输入,否则同步失败 2.解决 控制面板-windows用户-凭据管理器-添加凭据-从上到下一次输入  ip ...

  2. mybatis pagehelper 分页 失效

    pagehelper 不分页几种情况的解决方法 - web洋仔 - CSDN博客https://blog.csdn.net/csdn___lyy/article/details/77160488 分页 ...

  3. JDBC DataSource

    What is DataSource? 作为 DriverManager 设施的替代项,DataSource 对象是获取连接的首选方法.实现DataSource 接口的对象通常在基于 Java(TM) ...

  4. 如何解决Access操作或事件已被禁用模式阻止

    操作或事件已被禁用模式阻止.本来是Access安全设置的一部分,可以防止一些危险性的宏自动运行损坏数据,但是如果是自己在设计或是修改Access数据库的时候,这个就比较烦人了,一次次的提示,每次都需要 ...

  5. .frm文件怎么导入到数据库

    如题想搞个私服游戏,但是数据库文件按文档的操作方法行不通.只能自行导入. 其实.frm文件就是mysql表结构文件,你拷贝data那一块的文件到你电脑安装的mysql的data文件下就行了. 一.首先 ...

  6. php cURL error 60: SSL certificate problem: unable to get local issuer certificate 解决办法

    错误例子如下: php5.6以上的版本会出现这种问题 关于“SSL证书问题:无法获取本地颁发者证书”错误.很明显,这适用于发送CURL请求的系统(并且没有服务器接收请求) 1)从https://cur ...

  7. Django model中的save后的return

    先给结论吧:在Django model的操作函数中,obj.save()后再执行return obj会返回obj的ID. 看例子: ... def create_session(self,bind_h ...

  8. Spring Boot 使用MockMvc对象模拟调用Controller

    功能实现之后,我们要养成随手写配套单元测试的习惯,这在微服务架构中尤为重要.通常,我们实施微服务架构的时候,已经实现了前后端分离的项目与架构部署.那么在实现后端服务的时候,单元测试是在开发过程中用来验 ...

  9. DELPHI 数据库操作类(工具类)

    DELPHI 数据库连接类 做的时候目地是可以通过类的创建和释放进行数据库的短连接,在做服务端的时候每一个请求都通过类生成一个数据连接 unit UnDm; interface uses SysUti ...

  10. 【esp8266】技术汇总帖

    https://blog.csdn.net/xh870189248/article/details/80027961 这哥们 牛