【多图警告】彻底搞懂浮点数

这篇具有很好参考价值的文章主要介绍了【多图警告】彻底搞懂浮点数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

float类型,在很多地方没有看明白,多文字也看得人心烦。

最近不是很愿意看多文字的博客和技术文章了,但确实不是什么好事。

要改。

本文用大量图片讲解了浮点数在计算机中的存储方式以及浮点数的最值、精度等问题,文末有测试用的C++程序。

从二进制表示小数说起

我们先不让小数点“浮动”,表示12.625这个数字。

小数点左边好说,直接十进制转二进制就好了:

12->1100

小数点右边呢?

其实和十进制一样,二进制的小数点左边从左到右分别表示的是23、22、21、20,现在跳到小数点右边,自然也就有了2-1、2-2、2-3……这和我们早就熟悉了的十进制小数也是一样的思想,只不过二进制只有0、1两种数字。

浮点数,打破砂锅系列,c++,数据结构

再到科学计数法

十进制的科学计数法是人人都会了:
12.625 ( 常 规 表 示 ) = 1.2625 × 1 0 1 ( 常 规 科 学 计 数 法 ) = 1.2625 E 1 ( 源 码 中 的 表 示 法 ) 12.625(常规表示)=1.2625\times10^{1}(常规科学计数法)=1.2625E1(源码中的表示法) 12.625()=1.2625×101()=1.2625E1()
浮点数,打破砂锅系列,c++,数据结构

然而计算机很憨,不认识十进制,只能存储二进制,二进制1100.101的科学计数法如何表示呢?

把十进制的思想直接用上就好,只不过底数要从10改成2:

浮点数,打破砂锅系列,c++,数据结构

浮点数在计算机中的存储方式

顺着讲下来,想必也能看出来浮点数在二进制中是通过二进制的科学计数法存储的了,我们需要的有这么三部分:

  1. 符号。表示浮点数的正负号,1bit足够;
  2. 数字部分(尾数部分)。即上图中蓝色的部分,因为除了0以外,蓝色数字部分小数点左边的首位都是1,所以我们直接存储小数点后面的部分,即尾数部分(本例为100101)。
  3. 指数部分。即上图中红色的部分,是一个有符号整数(本例为3)。

我们以4字节的float为例:

  1. 符号位。占1bit,0表示正数,1表示负数;

  2. 指数部分。占后续的8bit,以+127偏移的方式存储,比如指数部分若为-3,则存储为130(10进制),即二进制的1000 0010

    为什么指数部分以偏移的方式存储而非以二进制补码方式存储?

  3. 尾数部分。占后续的23bit,本例中为100101

浮点数,打破砂锅系列,c++,数据结构

总结:记符号位为Sign,指数部分为Exponent,尾数部分为Fraction,则浮点数为:

浮点数,打破砂锅系列,c++,数据结构

注:指数部分的0x00、0xff特殊值在下一节讲。

8字节的double双精度浮点数,符号位不变,指数部分变为11bit,尾数部分变为52bit,其他同理。

浮点数的特殊值和最值

本节回答这么几个问题:

  • 浮点数最大(绝对值最大)可以表示到什么值?
  • 浮点数如何处理越界的值?
  • 浮点数如何表示0?

指数部分的特殊值: 0xff 和 0x00

浮点数标准规定1111 1111(0xff)被留作特殊值用,对于越界的无法处理的值,也就是inf-inf(infinity),尾数部分均为0。

浮点数,打破砂锅系列,c++,数据结构

对于指数部分为0xff,尾数部分非0x00,则留作NaN,用于表达对负数开平方、对负数求对数、inf / inf这类无效数字。


然后是0x00,因为之前讲的尾数部分默认是把小数点前一位看成1的,无法表示0,浮点数标准规定,当指数部分和尾数部分为全0时,浮点数表示0,由于符号位的存在,也就出现了±0这种东西,可以看做是相等的,但是某些情况会对其区分,这里我们不做讨论。

浮点数,打破砂锅系列,c++,数据结构

当指数部分为0x00,而尾数部分非0时,浮点数标准规定这样的浮点数为非规格化浮点数(subnormal number),之所以非规格化,是因为此时的浮点数精度并不能达到要求,用我的理解说:小数点就不再是浮动的了,不配叫浮点数,是不正常的浮点数。二进制科学计数是这样的:
( − 1 ) s i g n × 0. f r a c t i o n × 2 − 126 (-1)^{sign}\times0.fraction\times2^{-126} (1)sign×0.fraction×2126

浮点数的最值

首先看正数,要找浮点数能正常表示的最大值,指数部分首先要尽量大,然而浮点数标准规定,指数部分最大只能到1111 1110(0xfe),这是由于1111 1111(0xff)被留作特殊值用了,也就是inf,对于越界的无法处理的值,会显示为inf-inf,尾数部分均为0。这也就解决了第二个问题。

我们继续看最大值:指数部分最大可以取到1111 1110(0xfe)即127,尾数部分最大可以取到23个1,可以表示的最大数如下,十进制表示3.402823466e+38,程序中可以用FLT_MAX得到;
1.11111111111111111111111 × 2 127 ( 正 数 f l o a t 可 以 取 到 的 最 大 值 ) 1.111 1111 1111 1111 1111 1111\times2^{127}(正数float可以取到的最大值) 1.11111111111111111111111×2127(float)
不考虑负数的情况下,float也有可以表示的最小正数,而且分为规格化和非规格化的浮点数。

对于规格化的浮点数,指数部分尽量小,最小到0x01,即-126,尾数部分也尽量小,此时取到最小值,十进制表示1.175494351e-38,程序中可以用FLT_MIN得到:
1.00000000000000000000000 × 2 − 126 ( 规 格 化 正 数 f l o a t 可 以 取 到 的 最 小 值 ) 1.000 0000 0000 0000 0000 0000\times2^{-126}(规格化正数float可以取到的最小值) 1.00000000000000000000000×2126(float)
然而在指数部分为0x00时,非规格化浮点数其实可以取到比FLT_MIN更小的正数,十进制表示1.401298464e-45F,程序中用FLT_TRUE_MIN可得到:
0.00000000000000000000001 × 2 − 126 ( 非 规 格 化 正 数 f l o a t 可 以 取 到 的 最 小 值 ) 0.000 0000 0000 0000 0000 0001\times2^{-126}(非规格化正数float可以取到的最小值) 0.00000000000000000000001×2126(float)
正数的最值有了,负数的最值只需要把符号位置1就好。


值得注意的是,浮点数的最值并不代表浮点数可以取到区间内每一个值,浮点数是有精度的,也就是下一节要提到的浮点数精度问题

浮点数精度问题

本节将回答以下问题:

  • 为什么如下代码会输出False

    double x = 0.0; // float也一样
    for (int i = 0; i < 10; ++i) {
        x += 0.1;
    }
    double y = 1.0; // float也一样
    cout << (x == y ? "True" : "False") << endl;
    
  • 为什么如下代码又会输出True

    bool isEqual(float a, float b) {
        return a == b;
    }
    
    int main() {
        double x = 0.0;
        for (int i = 0; i < 10; ++i) {
            x += 0.1;
        }
        double y = 1.0;
        cout << (isEqual(x, y) ? "True" : "False") << endl;
        return 0;
    }
    
  • 我们应如何对浮点数的精度进行取值?

赶一下五一创作勋章,后面的部分后续再补上。文章来源地址https://www.toymoban.com/news/detail-558803.html

测试用程序

// 打印二进制形式的float数,并将指数部分、尾数部分间隔开,稍微改一下就可以间隔打印double类型
template<class T>
void printbybit(const T &ob) {
    char *p_e = (char *) &ob;
    char *p = p_e + sizeof(T) - 1;
    int cnt = 0;
    for (; p >= p_e; p--) {
        for (int i = 7; i >= 0; i--) {
            if (cnt == 1 || cnt == 9) cout << ' '; // 打出间隔,方便查看各个部分值
            cout << (((*p) & (1 << i)) ? 1 : 0);
            cnt++;
        }
    }
    cout << endl;
}

到了这里,关于【多图警告】彻底搞懂浮点数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 一文彻底搞懂JSON数据

    什么是JSON,为什么需要JSON,JSON的3种形式,JSON常用的方法等 TIP JSON指的是全称是:javascript对象表示法 JSON是Ajax发送和接收数据的一种格式 JSON是一种轻量级的数据交互格式, 其为字符串类型 (面试题会考到) JSON是一种语法,用来序列化对象、数组、数值、字符串、布尔值和

    2024年02月06日
    浏览(39)
  • 20,000+ 字,彻底搞懂 Kafka!

    1、解耦合 2、异步处理 例如电商平台,秒杀活动。 一般流程会分为: 风险控制 库存锁定 生成订单 短信通知 更新数据 通过消息系统将秒杀活动业务拆分开,将不急需处理的业务放在后面慢慢处理; 流程改为: 风险控制 库存锁定 消息系统 生成订单 短信通知 更新数据 3、

    2024年02月11日
    浏览(27)
  • 【算法】一文彻底搞懂ZAB算法

    最近需要设计一个分布式系统,需要一个中间件来存储共享的信息,来保证多个系统之间的数据一致性,调研了两个主流框架Zookeeper和ETCD,发现都能满足我们的系统需求。 其中ETCD是K8s中采用的分布式存储,而其底层采用了RAFT算法来保证一致性,之前已经详细分析了Raft算法

    2024年02月02日
    浏览(48)
  • mysql彻底卸载干净的5个步骤,超多图超详细保姆级教程最新教程新手小白轻松上手

    ✨ 原创不易,还希望各位大佬支持一下! 👍 点赞,你的认可是我创作的动力! ⭐️ 收藏,你的青睐是我努力的方向! ✏️ 评论,你的意见是我进步的财富! mysql8和mysql5的安装过程都有!!!超多图超详细保姆级教程最新教程新手小白轻松上手(点击跳转) mysql彻底卸载

    2024年02月03日
    浏览(52)
  • 彻底解决:IDEA java: 警告: 源发行版 17 需要目标发行版 17

    📢CSDN博客主页:低山高梧桐-致力于做最优质的内容 📢如果涉及到版权问题,烦请联系作者删除! 📢如果文章有谬误,烦请您指出斧正,作者致力于做最好的博客。 📢整合:低山高梧桐 首发于CSDN 欢迎点赞👍收藏⭐留言打扰📝 先来看一下报错信息 IDEA java: 警告: 源发行

    2024年02月03日
    浏览(47)
  • 2分钟彻底搞懂“高内聚,低耦合”

    💗推荐阅读文章💗 🌸 JavaSE系列 🌸👉1️⃣《JavaSE系列教程》 🌺 MySQL系列 🌺👉2️⃣《MySQL系列教程》 🍀 JavaWeb系列 🍀👉3️⃣《JavaWeb系列教程》 🌻 SSM框架系列 🌻👉4️⃣《SSM框架系列教程》 🎉本博客知识点收录于🎉👉🚀《SSM框架系列教程》🚀—✈️01【高内聚

    2024年03月23日
    浏览(44)
  • 彻底搞懂 PHP 运算符 ?: 和 ??

    ?: 称之为短三元运算符,它是我们熟悉的三元运算符(也叫做条件运算符)的一种特殊写法,也就是省略了三元运算符中间的部分。 复习一下三元表达式的语法: (expr1) ? (expr2) : (expr3) 在 expr1 求值为 true 时的值为 expr2,在 expr1 求值为 false 时的值为 expr3。 省略三元运算符中间

    2024年02月11日
    浏览(31)
  • 一文彻底搞懂ssh的端口转发

    端口转发是突破网络域隔离的一个手段。在学习这个知识的时候需要不断自问 为什么需要端口转发? 应用场景是什么呢? SSH 隧道或 SSH 端口转发可以用来在 客户端和服务器之间建立一个加密的 SSH 连接 如下图,通过它来把本地流量转发到服务器端,或者把服务器端流量转发

    2023年04月22日
    浏览(33)
  • 一文彻底搞懂Maven配置(终结版)

    下载安装 提示:安装之前需要先确认好自己需要哪个版本的maven,避免浪费时间。 官网下载:https://maven.apache.org/download.cgi 历史版本下载:https://archive.apache.org/dist/maven/maven-3/ maven配置setting.xml localRepository 该值表示构建系统本地仓库的路径 interactiveMode 表示maven是否需要和用

    2024年02月04日
    浏览(30)
  • 彻底搞懂贝塞尔曲线的原理

    贝塞尔曲线介绍 我们在前面讲了绘制自定义曲线,而实际开发过程还会遇到更复杂的图形绘制,比如下面的这些图形: 这时候就需要用到贝塞尔曲线了。下面是百科关于贝塞尔曲线的介绍。 贝塞尔曲线就是这样的一条曲线,它是依据四个位置任意的点坐标绘制出的一条光滑

    2024年02月20日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包