浮点数在内存中的存储结构

浮点数在内存中的存储可以参考《IEEE754标准》https://people.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF

参考博文:IEEE754详解(最详细简单有趣味的介绍)-CSDN博客



单精度float占内存4字节,最高位bit31表示符号位,指数位bit23~bit30占用8个bit位,剩余的23个bit表示尾数。

双精度double占用内存8字节,最高位bit63表示符号位,指数位bit52~bit62占用11个bit位,剩余的52个bit表示尾数。

字节长度符号位S指数位E尾数M
单精度4字节1 [31]8 [30-23]23 [22-0]
双精度8字节1 [63]11 [62-52]52 [51-0]



任何一个数,都可以使用不同的权重表示,最终的计算结果公式如下:

假如有一个数:AnAn-1An-2...A2A1B1B2...Bn-1Bn,其中A1~An表示整数部分的每一位数,B1~Bn表示小数部分的每一位数。权重为Q,则

S = An*Q^(n-1)+An-1*Q^(n-2)+...+A2*Q^1+A1*Q^0+B1*Q^(-1)+B2*Q^(-2)+...Bn-1*Q^(-(n-1))+Bn*Q^(-n)

例如十进制数:123.456,对应的十进制数是

S = 1*10^2+2*10^1+3*10^0+4*10^(-1)+5*10^(-2)+6*10^(-3)

再比如二进制数:1011.011,对应的十进制数是

S = 1*2^3+0*2^2+1*2^1+1*2^0+0*2^(-1)+1*2^(-2)+1*2^(-3) = 11.365

再比如八进制数:56.76,对应的十进制是

S = 5*8^1+6*8^0+7*8^(-1)+6*8^(-2) = 46.96875

将10进制数转为其他进制,比如将整数为A,小数为B变为b进制(123.456:整数A=123,小数B=0.456),则步骤如下:

整数部分:

1. A除以b得到商B和余数R1

2. B除以b得到商C和余数R2

3. .................................Rn-1

4. 重复2,直到商为0,余数为Rn

则整数部分是:RnRn-1...R2R1

小数部分:

1. 小数B乘以b得到积B',再将B'拆分成整数部分F1和小数C

2. 小数C乘以b得到积C',再将C'拆分成整数部分F2和小数D

3. 小数D乘以b得到积D',再将D'拆分成整数部分F3和小数E

4. 重复上述过程,直到小数为0。(有可能永远不会等于0)

则小数部分是:F1F2F3...Fn-1Fn..........

所以最终10进制转为其他进制的结果就是:RnRn-1...R2R1.F1F2F3...Fn-1Fn.........

注意R1和F1之间有一个小数点。



有了上面的基础认知后,我们来看看浮点数是如何存储在内存中的

先说单精度

指数是8位bit,所以可以表示为0~255,但是指数存在正数和负数,所以IEEE754规定,存入内存时E的真实值必须再加上一个中间数127(2^7-1)

再说双精度

指数是11位bit,所以可以表示为0~2047,但是指数存在正数和负数,所以IEEE754规定,存入内存时E的真实值必须再加上一个中间数1023(2^11-1)

例:2^10的E是10,所以保存为32位浮点数的时候,E必须保存为10+127=137,即10001001

保存为64位浮点数的时候,E保存为10+1023=1033,即10000001001



根据《IEEE754标准》规定,浮点数在计算机中是以科学计数表示方法存储的,例如123.456表示为1.23456*10^2

下面使用例子来说明存储浮点数(正数和负数计算方式一样,唯一区别就是最高bit位不同而已)

十进制数:123.456

按照公式,它的二进制是:1111011.0111010010111100011010100111111011111001110110.....后面省略不计算

它的科学表示方式是1.1110110111010010111100011010100111111011111001110110.....*2^6所以如下(以单精度为例):

符号位:S=0

指数位:E=6+127=133,二进制是 10000101

尾数:M=1.1110110111010010111100011010100111111011111001110110......按照科学计数表示法来规定,小数点前面一定是1,所以IEEE754为了节省1一个bit,默认将小数点前面的1省略掉,所以实际M=1110110111010010111100011010100111111011111001110110......,右因为单精度的M只占23位,所以M=11101101110100101111001(第24位是1,需要进一位,遵循逢二进一原则)

所以最终的123.456在内存中的二进制是:

0 10000101 11101101110100101111001=0x42F6E979

十进制数:0.0456

二进制为:0.000010111010110001110001000011001011001010010101111010011110000...

科学表示:1.0111010110001110001000011001011001010010101111010011110000...*2^(-5)所以如下(单精度):

符号位:S=0

指数位:E=-5+127=122,二进制是 01111010

尾数:M=01110101100011100010001

所以最终的0.0456在内存中的二进制是:

0 01111010 01110101100011100010001=0x3D3AC711



十进制浮点数转为二进制存储的计算步骤:

1. 确定符号位,正数S=0,负数S=1

2. 将正数转为二进制表示,例如123.456=1111011.0111010010111100011010100111111011111001110110.....

0.0456=0.000010111010110001110001000011001011001010010101111010011110000...

3. 将小数点进行左移/右移e位,使得小数点前面有且仅有一个有意义的数字1,即:变为科学计数表达形式。(左移得到的指数是正数,右移得到的是负数)

123.456的二进制小数点需要左移6位

0.0456的二进制小数点需要右移5位

4. 指数加上中间数base(单精度base=127,双精度base=1023)

    E = e+base

   123.456的E=6+127=133                 0.0456的E=-5+127=122

5. 将科学计数法表示的二进制数,从小数点后面第一位开始(因为小数点前面肯定是1,为了节省1bit,所以去掉),从左到右依次填充到尾数位最后一位遵循逢二进一原则,从而得到M

6. 将S  E   M分别填充到对应的位,即可得到二进制存储。



内存中关于二进制转为浮点数

根据国际标准 IEEE754,任意一个浮点数都可以用如下形式来表示:

V = (-1)^S * M * 2^E。
符号 S 决定浮点数的是负数(S=1)还是正数(S=0),由一位符号位表示。
有效数M是一个二进制小数,它的范围在1~2之间。
指数E是2的幂,可正可负,作用是对浮点数加权,由8位或11位的指数域表示。

浮点数的二进制解析有以下解码格式
指数域E尾数域M说明
格式化值------

符合一般公式

此时的 E = E-base

非格式化值全是1全是0表示无穷
全是1不全是0表示NaN(不是一个有效数字)
特殊值全是0不全是0

此时的E=1-base。

M不再加上第一位的1,而是还原为0.xxxxxx的小数

0全是0全是0固定值0
--将整数转为浮点数float
local function Hex2Float(iValue)--[[--以下计算方法参考: https://docs.oracle.com/javase/6/docs/api/java/lang/Float.html#floatToIntBits(float)iValue = iValue&0xFFFFFFFFif 0x7f800000==iValue then --正无穷大return math.hugeelseif 0xff800000==iValue then --负无穷大return -math.hugeelseif (0x7f800001<=iValue and iValue<=0x7fffffff) or (0xff800001<=iValue and iValue<=0xffffffff) then --不是一个有效的数字return nilend--是一个有效的数字local bits = iValuelocal sif (bits>>31)&0x1==0 then s = 1else s = -1endlocal e = (bits>>23) & 0xfflocal mif 0==e then m = (bits & 0x7fffff) << 1else m = (bits & 0x7fffff) | 0x800000endreturn s*m*(2^(e-150)) -- s * m/(2^23) * 2^(e-127) 中间一部分相当于是十进制里面的 123/100=1.23一个操作]]----[[--详细解释实现原理--s e m分别占位: 1 8 23iValue = iValue&0xFFFFFFFFlocal s = (iValue>>31)&0x01local e = (iValue>>23)&0xFFlocal m = iValue&0x7FFFFFlocal S = slocal E = e-127local M = 1if e==0 and m==0 then --指数和尾数都是0的,表示0return 0.0 elseif e==0xFF and m==0 then --指数全是1,而尾数都是0的,表示无穷if s==1 thenreturn -math.hugeelsereturn math.hugeendelseif e==0xFF and m~=0 then --指数全是1,而尾数不全是0的,表示NaN(不是一个数)return nilelseif e==0 and m~=0 then --指数全是0,而尾数不全是0的,表示一种非规格化的数字E=-126 -- 1-127M = 0endfor i=1,23 doif (m>>(23-i))&0x01==0x01 thenM = M+2^(-i)endend-- f = (-1)^s * m * 2^ereturn ((-1)^S) * M * (2^E)]]--local binary = {}for i=3,0,-1 dotable.insert(binary,(iValue>>(i*8))&0xFF)endlocal v,pos = string.unpack(">f", string.char(table.unpack(binary)))return v
end

--将整数转为浮点数double
local function Hex2Double(iValue)--[[--以下计算方法参考: https://docs.oracle.com/javase/6/docs/api/java/lang/Double.html#doubleToLongBits(double)iValue = iValue&0xFFFFFFFFFFFFFFFFif 0x7ff0000000000000==iValue then --正无穷大return math.hugeelseif 0xfff0000000000000==iValue then --负无穷大return -math.hugeelseif (0x7ff0000000000001<=iValue and iValue<=0x7fffffffffffffff) or (0xfff0000000000001<=iValue and iValue<=0xffffffffffffffff) then --不是一个有效的数字return nilend--是一个有效的数字local bits = iValuelocal sif (bits>>63)&0x1==0 then s = 1else s = -1endlocal e = (bits>>52) & 0x7fflocal mif 0==e then m = (bits & 0xfffffffffffff) << 1else m = (bits & 0xfffffffffffff) | 0x10000000000000endreturn s*m*(2^(e-1075))]]----[[--详细解释实现原理--s e m分别占位: 1 11 52iValue = iValue&0xFFFFFFFFFFFFFFFFlocal s = (iValue>>63)&0x01local e = (iValue>>52)&0x7FFlocal m = iValue&0xFFFFFFFFFFFFFlocal S = slocal E = e-1023local M = 1if e==0 and m==0 then --指数和尾数都是0的,表示0return 0.0 elseif e==0x7FF and m==0 then --指数全是1,而尾数都是0的,表示无穷if s==1 thenreturn -math.hugeelsereturn math.hugeendelseif e==0x7FF and m~=0 then --指数全是1,而尾数不全是0的,表示NaN(不是一个数)return nilelseif e==0 and m~=0 then --一种非规格化的数字E=-1022 -- 1-1023M = 0endfor i=1,52 doif (m>>(52-i))&0x01==0x01 thenM = M+2^(-i)endend-- f = (-1)^s * m * 2^ereturn ((-1)^S) * M * (2^E)]]--local binary = {}for i=7,0,-1 dotable.insert(binary,(iValue>>(i*8))&0xFF)endlocal v,pos = string.unpack(">d", string.char(table.unpack(binary)))return v
end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/39661.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国家海岸线变化评估:新英格兰和中大西洋沿岸海岸线的历史变化

National Assessment of Shoreline Change: Historical Shoreline Change along the New England and Mid-Atlantic Coasts 国家海岸线变化评估&#xff1a;新英格兰和中大西洋沿岸海岸线的历史变化 摘要 海滩侵蚀是美国许多公海沿岸的一个长期问题。随着沿岸人口的不断增加…

永辉超市购物卡有什么用?

感觉现在在超市买东西&#xff0c;还不如网购 这不&#xff0c;端午的时候&#xff0c;朋友送的永辉卡&#xff0c;一直没时间去用&#xff0c;我总担心过期 但是去了超市后&#xff0c;又不知道买什么&#xff0c;最后空手而归 还好收卡云可以回收永辉卡&#xff0c;两张三…

《C++20设计模式》适配器模式经验分享

文章目录 一、前言二、对于接口的讨论三、实现1、对象适配器1.1 UML类图1.2 实现 2、类适配器 四、最后 一、前言 从适配器模式开始就是类的组合聚合&#xff0c;类与类之间结构性的问题了。 适配器模式解决的问题&#xff1a; 适配器模式能够在不破坏现有系统结构的情况下&a…

mapreduce实现bean的序列化与反序列化

目录 序列化&#xff08;Serialization&#xff09; 反序列化&#xff08;Deserialization&#xff09; 事例操作 UserSale 重写序列化方法 重写反序列化 重写toString方法 SaleMapper SaleReducer SaleDriver 序列化&#xff08;Serialization&#xff09; 序列化是将…

【后端面试题】【中间件】【NoSQL】MongoDB的配置服务器、复制机制、写入语义和面试准备

MongoDB的配置服务器 引入了分片机制之后&#xff0c;MongoDB启用了配置服务器(config server) 来存储元数据&#xff0c;这些元数据包括分片信息、权限控制信息&#xff0c;用来控制分布式锁。其中分片信息还会被负责执行查询mongos使用。 MongoDB的配置服务器有一个很大的优…

WPF----自定义滚动条ScrollViewer

滚动条是项目当中经常用到的一个控件&#xff0c;大部分对外项目都有外观的需求&#xff0c;因此需要自定义&#xff0c;文中主要是针对一段动态的状态数据进行展示&#xff0c;并保证数据始终在最新一条&#xff0c;就是需要滚动条滚动到底部。 1&#xff0c;xaml中引入 <…

zxing-cpp+OpenCV根据字符串生成条形码

编译构建 需要使用到 CMake、Git、GCC 或 MSVC。 github 链接&#xff1a;https://github.com/zxing-cpp/zxing-cpp 编译之前请确保&#xff1a; 确保安装了 CMake 版本 3.15 或更高版本。 确保安装了与 C17 兼容的编译器(最低VS 2019 16.8 / gcc 7 / clang 5)。 编译构建…

Python面试宝典第4题:环形链表

题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。如果存在环 &#xff0c;则返回 true 。 否则&#xff0c;返回 false 。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xf…

重写父类方法、创建单例对象 题目

题目 JAVA27 重写父类方法分析&#xff1a;代码&#xff1a; JAVA28 创建单例对象分析&#xff1a;代码&#xff1a; JAVA27 重写父类方法 描述 父类Base中定义了若干get方法&#xff0c;以及一个sum方法&#xff0c;sum方法是对一组数字的求和。请在子类 Sub 中重写 getX() 方…

AI智能体|AI打工我躺平!使用扣子Coze智能体自动生成和发布文章到微信公众号(一)

大家好&#xff0c;我是无界生长&#xff0c;国内最大AI付费社群“AI破局俱乐部”初创合伙人。这是我的第 44 篇原创文章——《AI智能体&#xff5c;AI打工我躺平&#xff01;使用扣子Coze智能体自动生成和发布文章到微信公众号&#xff08;一&#xff09;》 AI智能体&#xf…

《涅朵奇卡:一个女人的一生》读后感

这周的计划是看完海明威的《丧钟为谁而鸣》&#xff0c;但是因为下班晚&#xff0c;而且书的体量大&#xff0c;所以只看了一半。本来以为这周的阅读计划完不成了&#xff0c;不料昨天加完班后拿起新到的《涅朵奇卡&#xff1a;一个女人的一生》&#xff0c;不自觉就陷进去了&a…

端口聚合基础知识

一、什么是端口聚合 端口聚合是将多个物理端口捆绑在一起&#xff0c;形成一个逻辑链路&#xff0c;以实现带宽增加、提高冗余和负载均衡的技术。端口聚合&#xff0c;也称为以太通道&#xff08;Ethernet Channel&#xff09;&#xff0c;主要用于交换机之间的连接。在具有多…

开发数字药店APP实战:互联网医院系统源码详解

本篇文章&#xff0c;笔者将深入探讨如何开发一个功能完善的数字药店APP&#xff0c;并详细解析互联网医院系统的源码实现。 一、数字药店APP的需求分析 应具备以下基本功能&#xff1a; 用户注册与登录 药品搜索与浏览 在线下单与支付 订单管理 健康咨询与远程医疗 个人…

partition()方法——分割字符串为元组

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 partition()方法根据指定的分隔符将字符串进行分割。如果字符串中包含指定的分隔符&#xff0c;则返回一个3元的元组&#xff0c;第一个为…

Perl 语言开发(四):条件语句

目录 1. 概述 2. if 语句 3. else 语句 4. elsif 语句 5. unless 语句 6. 嵌套条件语句 7. 三元运算符 8. 智能匹配运算符 9. given-when 语句 10. 条件修饰符 11. 高级条件语句应用 11.1 数据验证 11.2 配置文件解析 11.3 异常处理 12. 条件语句的最佳实践 12…

Spring Boot+Mybatis Plus 使用Redis实现二级缓存具体步骤以及代码

下面是使用Spring BootMybatis Plus和Redis实现二级缓存的具体步骤和代码示例&#xff1a; 1. 首先&#xff0c;确保你已经添加了Spring Boot、Mybatis Plus和Redis的依赖。 2. 在Spring Boot的配置文件中添加Redis的配置&#xff0c;如下所示&#xff1a; yaml spring: r…

wordpress:更新网站域名后后页面无法访问,页面媒体文件异常(已解决)

WordPress 在数据库中存储了许多配置信息,包括网站的域名。如果更新了域名,但数据库中的旧域名没有更新,WordPress 将无法正确生成页面链接或重定向访问请求。 一、更新域名 在wp-config.php 文件中,添加或更新你的新域名! define(WP_HOME, http://172.18.214.195:32520…

Linux_fileio学习

参考韦东山老师教程&#xff1a;https://www.bilibili.com/video/BV1kk4y117Tu?p12 目录 1. 文件IO函数分类2. 函数原型2.1 系统调用接口2.2 标准IO接口 3. fileio内部机制3.1 系统调用接口内部流程3.1 dup函数使用3.2 dup2函数使用 4. open file4.1 open实例4.2 open函数分析…

Cocos如何跟Android通信?

点击上方亿元程序员+关注和★星标 引言 Cocos如何跟Android通信 大家好,相信小伙伴们通过阅读笔者前几期的文章**《Cocos打安卓包打不出来?看看这个》,对Cocos**如何打安卓包有了一定的了解。 但是,除了把安卓包打出来,另外还有一个重要的就是要能够调用安卓提供的Java方…

华为HCIP Datacom H12-821 卷21

1.单选题 以下关于PIM-SM中SPT切换的描述,错误的是哪一项? A、若所有组播流量都经过RP路由器,则RP路由器可能成为数据转发的瓶颈 B、SPT路径最短,转发性能更优 C、SPT 切换完成后,组播流量依然经过 ReT 树 D、RPT 树可能不是组播流量转发的最优路径 正确答案: C 解析…