在CSDN上发现一篇写的不错的文章,做一个备忘。原文地址:https://blog.csdn.net/nethibernate/article/details/6120382

先说一下计算机中二进制的算法:

  • 整数 
    整数的二进制算法大家应该很熟悉,就是不断的除以2取余数,然后将余数倒序排列。比如求9的二进制: 
    9/2=4 余 1 
    4/2=2 余 0 
    2/2=1 余 0 
    1/2=0 余 1 
    一直计算到商为0为止,然后将得到的余数由下到上排列,就得到了9的二进制:1001。 
    从上面的算法我们可以看到,用整数除以2,最终都能够到0。因此,整数是可以用二进制来精确表示的。
  • 小数 
    小数的二进制算法和整数的大致相反,就是不断的拿小数部分乘以2取积的整数部分,然后正序排列。比如求0.9的二进制: 
    0.9*2=1.8 取 1 
    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.11100110011... 
    从小数的二进制算法中我们可以知道,如果想让这种算法停止,只有在小数部分是0.5的时候才可以,但是很不幸,这类的小数很少。所以大部分小数是很难用二进制来精确表示的。

------------------------我是分割线------------------------------

OK,有了上面的知识,我们进入正题:看看float类型在内存中是如何表示的。  
float类型又称为单精度浮点类型,在 IEEE 754-2008 中是这样定义它的结构的:

S     EEEEEEEE      FFFFFFFFFFFFFFFFFFFFFFF
31   30        23    22                               0

float类型总共4个字节——32位:

  1. 符号位
    其中最左边的为符号位,0为正,1为负。
  2. 指数
    接下来的E是指数,一共8位,也用二进制来表示。
  3. 尾数
    最后的F是小数部分,尾数正是由这23位的小数部分+1位组成的。(这个稍后解释)。

这里我们需要多说一下指数。虽然指数也是用8位二进制来表示的,但是IEEE在定义它的时候做了些手脚,使用了偏移来计算指数。

IEEE规定,在float类型中,用来计算指数的偏移量为127。也就是说,如果你的指数实际是0,那么在内存中存的就是0+127=127的二进制。稍后我们来看这个到底如何使用。

好了,看了这么多,我们该演示一下计算机如何将一个十进制的实数转换为二进制的。就拿6.9这个数字来举例吧。-_-||!

首先,我们按照上面说的方法,分别将整数和小数转换成对应的二进制。这样6.9的二进制表示就是110.1110011001100...。这里就看出来 了,6.9转换成二进制,小数部分是无限循环的,这在现在的计算机系统上是无法精确表示的。这是计算机在计算浮点数的时候常常不精确的原因之一。

其次,将小数点左移(或右移)到第一个有效数字之后。说的通俗些,就是把小数点移到第一个1之后。这样的话,对于上面的110.1110011001100...我们就需要把小数点左移2位,得到1.101110011001100...。

接下来的事情就有意思了。首先我们把得到的1.101110011001100..这个数,从小数点后第一位开始,数出23个来,填充到上面float内存 结构的尾数部分(就是那一堆F的地方),我们这里数出来的就是10111001100110011001100。这里又要发生一次不精确了,小数点后超出 23位的部分都将被舍弃,太惨了。

不过,这里有一个可能让大家觉得特别坑爹的事情,就是小数点前面的1也不要了。仔细看看上面的内存结构,确实没有地方存放这个1。原因是这样的:IEEE觉 得,既然我们大家都约定把小数点移动到第一个有效数字之后,那也就默认小数点前面一定有且只有一个1,所以把这个1存起来也浪费,干脆就不要了,以后大家 都这么默契的来就好。这也是为什么我上面说尾数是23位+1位的原因。

填充完尾数,该填充指数了。这个指数就是刚才我们把小数点移动的位数,左移为正,右移为负,再按照上面所说的偏移量算法,我们填充的指数应该是2+127=129。转换成8位二进制就是10000001。

最后,根据这个数的正负来填充符号位。我们这里是正数,所以填0。这样6.9的在内存中的存储结果就出来了:

0  10000001  10111001100110011001100

总结一下,实数转二进制float类型的方法:

A. 分别将实数的整数和小数转换为二进制
B. 左移或者右移小数点到第一个有效数字之后
C. 从小数点后第一位开始数出23位填充到尾数部分 
D. 把小数点移动的位数,左移为正,右移为负,加上偏移量127,将所得的和转换为二进制填充到指数部分
E. 根据实数的正负来填充符号位,0为正,1为负

如果需要把float的二进制转换回十进制的实数,只要将上面的步骤倒着来一边就行了。

------------------------我是分割线------------------------------

需要注意的东西:

  1. 23位尾数填充的问题
    虽然在IEEE754标准中我没有找到相应的描述,但是在实际处理的时候,截取23位尾数需要对第24位进行零舍一入的操作,至少在Java虚拟机中是这么做的。有兴趣的可以试试0.7f-0.6f。
  2. 运算时向右对阶操作的舍入问题
    这个也是在实际操作时遇到的问题。到目前为止我还无法确定向右对阶操作是否也进行了零舍一入的操作。有兴趣的可以试试9.6f-6.9f。
  3. 指数全零问题
    全部为零的指数说明当前所表示的是一个特殊的float数字。全零的float类型分为两种情况:

    • 尾数全零。此时代表当前float数为0。根据符号位,分为+0和-0。这两个在JVM上相等的。 
      这里需要解释一下。因为IEEE的默认1的问题,所以float类型没有办法表示0,因此只能在已有的规定上做一些强制性的规则来表示0,也就有了上面的这个全零的说法。
    • 尾数不全为零。此时说明当前的float数是一个非规格化的数。
  4. 指数全一问题
  5. 指数全部为一也说明这个float数是一个不寻常的数字。它也分为两种情况:  
    • 尾数全零。此时根据符号位的不同,分为正无穷(+infinity)和负无穷(-infinity)。注意,这两个东西在JVM中是不相等的。
    • 尾数不全为零。此时表示此float数纯粹不是一个数(NaN,Not a Number)。这个NaN也分为QNaN(Quiet NaN)和SNaN(Signalling NaN)。至于这两个NaN有什么区别,下面这段话倒是说明了,但是我没有这方面的知识,所以不敢妄加翻译,只好把原文放在这里:
      A QNaN is a NaN with the most significant fraction bit set. QNaN's propagate freely through most arithmetic operations. These values pop out of an operation when the result is not mathematically defined.
      An SNaN is a NaN with the most significant fraction bit clear. It is used to signal an exception when used in operations. SNaN's can be handy to assign to uninitialized variables to trap premature usage. 
      Semantically, QNaN's denote indeterminate operations, while SNaN's denote invalid operations. 
      最后一句话说的明白,QNaN就是一个不确定操作的结果,而SNaN纯粹就是一个非法的操作结果。

------------------------我是分割线-----------------------------

OK,废话了这么多,我觉得对float类型也大致有个了解了。float明白了以后,double类型也就好说了,基本和上面一样,只是指数和尾数的位数不一样而已。

转载于:https://www.cnblogs.com/java-ty/p/9754198.html

float在内存中是如何保存的相关推荐

  1. java set第n位_Java学习路线:float在内存中的存储

    Java学习路线:float在内存中的存储,最近在讲Java基础,讲到数据类型的转换,提到整数类型长字节类型数据放到短字节类型数据中时,由于字节数不够,会截断数据,所以需要程序员手动强制类型转换,例如 ...

  2. python里面的类和对象_Python中类和对象在内存中是如何保存?

    类以及类中的方法在内存中只有一份,而根据类创建的每一个对象都在内存中需要存一份,大致如下图: 如上图所示,根据类创建对象时,对象中除了封装 name 和 age 的值之外,还会保存一个类对象指针,该值 ...

  3. 【C语言进阶深度学习记录】三 浮点数(float) 在内存中的表示方法

    相信大多数人知道整形数在内存中的分布方式,而且也能很容易写出其二进制的形式,但是对于浮点数,估计知道的人并不是很多 今天学习在C语言中浮点数在内存中的表示方法 文章目录 1 浮点数在内存中的存储方式 ...

  4. Python中相同的值在内存中到底会保存几份

    Python采用基于值的内存管理模式,相同的值在内存中只有一份.这是很多Python教程上都会提到的一句话,但实际情况要复杂的多.什么才是值?什么样的值才会在内存中只保存一份?这是个非常复杂的问题. ...

  5. 【工具篇】float值在不同系统内存中的数值转换工具

    一.原理 float在内存中如何存储? float为浮点型,32位机器中占4字节共32bit,下标0-31. 31 位:符号位,正数为0,负数为1. 30 位:方向位.小数点左移位1,右移为0. 23 ...

  6. C语言 float、double数据在内存中的存储方式

    float在内存中占4个字节(32bit),32bit=符号位(1bit)+指数位(8bit)+底数位(23bit) 指数部分 指数位占8bit,可以表示数值的范围是0-255(表示0~255一共25 ...

  7. c语言double数据存储形式,C语言 float、double数据在内存中的存储方式

    float在内存中占4个字节(32bit),32bit=符号位(1bit)+指数位(8bit)+底数位(23bit) 指数部分 指数位占8bit,可以表示数值的范围是0-(表示0~255一共256个数 ...

  8. C语言浮点数据在内存中的存储方式

    float在内存中的存储遵循IEEE 754标准.在C/C++中,float类型占4个字节即32位 , 这32位分成了3部分: 符号位最为简单,如果你存储的是正数那么符号数就是0.如果是负数,则为1. ...

  9. C++ float的内存布局

    IEEE754标准中,浮点数的内存布局 以下仅以float(内存中占据4个字节,32bits)来说明,double(8个字节,64bits)同理,只是有细微的差别. float的内存分布 IEEE75 ...

  10. 德鲁伊 oltp oltp_内存中OLTP系列–简介

    德鲁伊 oltp oltp Introduced on SQL Server 2014, the new brand feature In-Memory OLTP a.k.a "Hekato ...

最新文章

  1. python太阳花绘图_python中turtle如何画太阳花?
  2. mpi4py多进程实例/举例
  3. nodejs express 加载html模板
  4. mysql2阶段提交具体实现_ShardingSphere 4.x 分布式事务之实现原理
  5. 初识Flink-从WorldCount开始
  6. End to End Sequence Labeling via Bi-directional LSTM CNNs CRF
  7. frameset和frame
  8. 如何通过js调用接口
  9. 《数学的思维方式与创新》课程感悟与总结
  10. 生成XML文件的步骤 解析XML文件
  11. 时间序列学习(1):平稳性、自相关性
  12. 国美金融APP如何提前结清所有贷款
  13. Android Studio 报错 Error:Some file crunching failed, see logs for details
  14. 2019/10/13中国工商银行笔试编程题
  15. 我九点钟上计算机课用英语怎么说,人教版四年级英语下册 第5次课 Unit 2 What time is it 时间的表达方式...
  16. 基础汇编语言程序设计
  17. 用JavaScript实现烟花效果
  18. linux dd销毁数据,dd命令详解(清除硬盘数据)
  19. mac安装软件的一般目录
  20. 又一年的五一劳动节!

热门文章

  1. 异常、模块、文件读写
  2. JSARToolKit5文档翻译
  3. 2021-07-01小程序01
  4. linux getopt_long函数,新手疑问:getopt_long()重入问题
  5. java制作大富翁游戏_JAVA大富翁游戏的设计+流程图+总结体设计图-论文.doc
  6. 如何查看linux是grub还lilo,Linux下找回忘记root口令(lilo/grub)的方法
  7. python语法学习第二天--条件与循环
  8. JDBC04 PreparedStatement
  9. mbr linux安装分区,linux – 在MBR扇区上安装GRUB或在启动分区上安装第一个扇区之间的区别?...
  10. java中max的意义_[Java] xms xmx XX:PermSize XX:MaxPermSize 参数意义解析