技术标签: 计算机基础 二进制如何表达小数 计算机如何存储小数 浮点数二进制
【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://blog.csdn.net/m0_69908381/article/details/137182814
出自【进步*于辰的博客】
参考笔记三,P56.1、P57.2。
注:为了阐述更加严谨,本篇文章中将使用一些二进制的相关概念,出自上篇博文。
前言
在解析Float类的源码时,我对MAX_VALUE/MIN_VALUE
的值很好奇,它们是怎么来的?于是利用我所知的二进制知识,尝试运算。一开工就发现没辙,因为我压根不知道小数的二进制是怎样表示、又是如何存储的。于是寻得一方案:
启发博文:浮点数(小数)在计算机中如何用二进制存储?(转发)。
这位博主的阐述专业且详细,下面我通过个人理解,尽量简明扼要地为大家阐明这个知识点。
正文
在开始之前,大家先看一张图。
Float 就是单精度,就是说 Float 变量由32
位(4字节)二进制表示。现在大家对这张图有所疑惑,无妨,我要表达的意思是,小数的二进制由三部分组成,与整数完全不同。换言之,给我们一组小数的二进制,我们无法直接看出它的 真值 \color{green}{真值} 真值是多少。因此,需要使用一种纸面上的二进制数表现形式。
如何运算出这种纸面上的二进制数表现形式? \color{grey}{如何运算出这种纸面上的二进制数表现形式?} 如何运算出这种纸面上的二进制数表现形式?
所谓纸面上,就是一目了然,也就是使用整数二进制的表现形式去表现小数。那位博主是这样说的:
二进制转换为十进制的方法就是各个位的数字与位权乘积之和。
什么是“位权”?这张图给了答案。
就是底数的指数幂。
答案很清楚了,可是如何运算浮点数的小数部分的二进制,难道使用如上“求和”的方法?当然可以,不过不太方便。
从另一位博主那儿“取经”得一方法:使用上图示例。整数部分照旧,是1011
,将小数部分0.1875
进行以下运算:
0.1875 * 2 = 0.3750 → 0
0.3750 * 2 = 0.7500 → 0
0.7500 * 2 = 1.5000 → 1
0.5000 * 2 = 1.0000 → 1
得0011
。
结论:
将小数部分乘以
2
,取结果的整数部分,如此反复,直至结果为0
,最后依次得到的整数部分就是小数部分的二进制。
PS:暂不懂其中原理,就挺好用。
补充一点 \color{red}{补充一点} 补充一点:那位博主将100
个 float 类型的0.1
相加,最终结果不是10.0
。
大家便可明了,无论二选一,0.1
都是无限小数。无论单双精度,都无法表示完全,必然有所缺失或增加(四舍五入),故是10.000002
。
成功了一半,可1011.0011
只是11.1875
在纸面上的二进制数表现形式。
浮点数 ( 小数 ) 在计算机中如何用二进制存储? \color{grey}{浮点数(小数)在计算机中如何用二进制存储?} 浮点数(小数)在计算机中如何用二进制存储?
回到第一张图,小数的二进制由符号、指数、尾数三部分组成,这说明必然有一个公式,将这三部分进行运算,从而得到 真值 \color{green}{真值} 真值。
公式是这样的:
二进制中基数(又称“底数”)是2
,自然不必考虑。小数内部构造的三部分正好与图中三个未知变量对应,下面我一一剖析。(以单精度为例)
符号部分占1
位,即0/1
。(PS:小数没有 补码 \color{brown}{补码} 补码之说)
指数部分(8
位)与尾数部分(23
位)又是如何表示小数的?
我们来探讨一下,看到 m * ne 这样的公式,给你11.1875
这个小数,你能想到哪些等式?
11.1875 = 111.875 * 10-1,m是
111.875
,n是10
,e是-1
11.1875 = 1.11875 * 101, m是1.11875
,n是10
,e是1
......
有问题么?这里是二进制,n 是2
,不是10
,故等式不能这么写。
可是要满足如下等式,m 是多少?
11.1875 = m * 2-1
11.1875 = m * 21
......
看到这样的等式,大家是否似曾相识?没错,位运算,也就是这样:
11.1875 = m * 2-1 = m >> 1
11.1875 = m * 21 = m << 1
......
明白了么?
可这里有个问题,因为位运算移动的位数e
是任意的,故 m
任意,则必然存在一个规范,用于限制e
的值。
规范定义:
尾数部分用的是“将小数点前面的值固定为
1
的正则表达式”。
什么是正则表达式?按照特定的规则来表示数据的形式的表达式。
这样就清楚了,规范就是“将小数点左边第一位固定为1
,其他为0
”。如此,e
就只有一个值。
PS:不过,对于那位博主将规范定义为“正则表达式”这一点,我的个人看法是,意思没错,可用词似乎不恰当,当时我就被误导了。当然,也可能是我的功底不扎实。
规范知道了,可尾数m
是多少呢?大家在上文的阅读中有没有注意到一个细节?就是“纸面上的二进制数表现形式”那儿,我最后说了一句:“成功了一半”。成功在哪?又何出此言?
其实,小数在纸面上的二进制数表现形式就是 m * 2e 的结果。以11.1875
为例:
11.1875 → 1011.0011
规范是“将小数点左边第一位固定为1
,其他为0
”,就是这样:
11.1875
→1011.0011
=1.0110011
<< 3 =1.0110011
* 23
这样,难道 m 是1.0110011
?当然不是,那位博主已阐明:
因此,m 是01100110000000000000000
。e 是3
。
对应到小数的内部构造,11.1875
的二进制是:
0 00000011 01100110000000000000000
这是正确答案吗?还不是。
运用以上方法,我们来计算一下0.1875
的二进制:
0.0011 原始数值
1.1 左移使个位为 1
1.10000000000000000000000 确保小数点后有23位
10000000000000000000000 仅保留小数点后的部分
得出,m 是10000000000000000000000
,e 是-3
。
因此,0.1875
的二进制是0 10000011 10000000000000000000000
。(指数e
使用的是“原码”,不是“补码”)
这样看来,似乎没有问题,可实际上指数部分还有点“门道”,其采用的是“无符号二进制”。
那位博主阐述道:
指数部分使用了 “ E X C E S S 系统表现” \color{green}{“EXCESS系统表现”} “EXCESS系统表现”。
什么是“EXCESS系统表现”?那位博主已阐述得很清楚,我就不赘述了。
总结:
11.1875
的二进制是0 10000010 01100110000000000000000
。0.1875
的二进制是0 01111100 10000000000000000000000
。PS:
本文完结。
上一篇:《二进制相关概念、运算与应用》。
文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib
文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang
文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些
文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器
文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距
文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器
文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn
文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios
文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql
文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...
文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120
文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数