%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 ![[02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示#^5vpyb1]] ![[02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示#^kkba4k]] ![[02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示#^6hnxfx]] ![[02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示#^5iwe2m]] ![[02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示#^wfptmz]] ![[02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示#^zo6fsb]] ![[02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示#^l5xzib]] #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later %% <br> # 定点数与浮点数 - **定点数**:小数点位置约定在**固定位置**的数; - 格式: $m.n$ ,其中 $m$ 为整数部分,$n$ 为小数部分 - **浮点数**:小数点位置约定为**可浮动**的数称为浮点数; - 格式:$\text { value }=(-1)^{\text {sign }} \times(1 \text {.fraction }) \times 2^{\text {exponent}}$ (**二进制下的浮点数格式**) > [!faq] <font color="#c0504d">❓ 计算机中如何表示一个小数?</font> > > 对于小数,如果采用**==定点数==表示法**,无论是**直接的==十进制小数表示==**,还**是直接的==二进制小数表示==**, > 在**给定长度编码下都无法有效地对某一些分数(无限小数)精确表示,近似地表示其精确值需要过多位数**。 > > 为此,计算机中需要采用一种 **==更有效==** 的 **==浮点数==表示法**,由此提出/应用了 **IEEE 754 标准中规定的浮点数二进制表示方法**。 ^5vpyb1 <br><br><br> # 定点数表示 ### 十进制下的小数表示 ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-EC00184C05176716EF58A60119247FB1.png|673]] > [!NOTE] 在有限长度的编码下,**十进制表示法不能准确地表示一些分数(无限小数)** ,例如 $1/3$ 和 $5/7$。 <br> ### 二进制下的小数表示 **小数的直接二进制表示**: $ \large b_mb_{m-1}\cdots b_{1}b_{0}.b_{-1}b_{-2}\cdots b_{-n-1}b_{-n} $ 其中, $b_i=\{0,1\}$ 是二进制位。 ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-1C859D2B4B1DFB3ACBE987DEC2BF1B67.png|325]] 这一二进制表示对应的**十进制数值**为: ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-DDC3C14FCD902B3D1201CDBE6B8DEA09.png|158]] 对于一个**二进制小数表示**: - 将其 **==小数点左移一位==** 意味着 **==除以== 2**,左移 $i$ 位意味着除以 $2^i$ - 将其 **==小数点右移一位==** 意味着 **==乘以== 2**,右移 $i$ 位意味着乘以 $2^i$ > [!note] 在有限长度的编码下,**二进制表示法只能表示可被写为 $x\times 2^y$ 的数** > > 例如,**$1/5$ 在十进制小数表示法下可以被精确表示为 $0.20$, > 但是在二进制表示法下却无法被精确表示**,而只能被近似表示: > > ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-455FD064EDCB60A3B013B69673209C1E.png|350]] <br> ### [十进制小数转二进制小数](https://www.runoob.com/w3cnote/decimal-decimals-are-converted-to-binary-fractions.html) ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-70851ACE9DBE5CA73E8DD5AA1A0BF1C8.png|275]] 方法:将十进制小数的**整数部分**和**小数部分**分别转为二进制形式: - **整数部分**转二进制:**==除二取余==**,**逆序**排列所得余数,得到整数部分的二进制表示 - **小数部分**转二进制:**==乘二取整==**,**顺序**排列取得的整数(0/1),得到小数部分的二进制表示 - 每次取整,直到**最后结果为 0**,或者**达到精度要求**。 > [!example] > > ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-57D5541267F419D4F534D81535B890C8.png|575]] > > <br><br> # 浮点数表示 **浮点数**:小数点位置约定为**可浮动**的数。 **浮点数**采用类似**科学计数法**的方式表示数值,**由尾数**(mantissa)、**基数**(通常为2)**和指数**(exponent)三部分组成。 **二进制下的浮点数格式**:$\text { value }=(-1)^{\text {sign }} \times(1 \text {.fraction }) \times 2^{\text {exponent}}$ - 尾数部分由 "**定点小数**" 表示 - 整数部分由 "**定点整数**" 表示 <br><br><br> # [IEEE 754 浮点数标准](https://zh.wikipedia.org/wiki/IEEE_754) IEEE 754 标准定义了**浮点数的存储格式和算术规范**,使得浮点数的运算在不同的计算机和平台上能够保持一致。 ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-8F76B829B87523C80D2AB1FE3B838A4A.png|700]] 其中,对浮点数值最主要的两种**二进制编码格式**是**32 位单精度表示** 和 **64 位双精度表示**。 视频讲解:[IEEE Standard for Floating-Point Arithmetic (IEEE 754) - YouTube](https://www.youtube.com/watch?v=_NFaYk9R9jI) 在 IEEE 754 浮点数编码标准下: - **==可被精确表示==** 的浮点数是 **==精度有限==**、**==非均匀分布==** 的; - **==不能被精确表示==** 的浮点数,会根据一定原则对其进行 "**==舍入==**(rouding)"——舍入为**能够被精确表示的浮点数**。 - 为 **==特殊值==** 定义了对应的特定二进制表示,特殊值包括: - **正无穷大**(+∞)、**负无穷大**(-∞) - **NaN**(不是一个数字) <br><br><br> # IEEE 754 标准下浮点数的二进制表示 ## 主要思想 > ❓<font color="#c0504d"> IEEE 754 是如何对浮点数进行表示的?</font> ^kkba4k 在 IEEE 754 标准下,**所有浮点数需要统一表示成如下规定的==二进制小数==形式**: $ \large V=(-1)^s\times M\times 2^E $ - **==符号==** $\large s$:1 为负,0 为正; - **==尾数==** $M$:为**二进制小数**,规定其范围为 $[1, 2)$ 或 $[0, 1)$: - **规范化值的形式**: $M={1.f_{m-1}f_{m-2}\dots f_{1}f_{0}}$;其中 $ 为有效数字位数 - **非规范化值形式**: $M={0.f_{m-1}f_{m-2}\dots f_{1}f_{0}}$; - ==**指数**== $E$:为**十进制整数**,表示对尾数 $M$ 的缩放; 通过对上述规定形式中的 **$s$, $M$, $E$ 分别进行特定的编码**得到对应的**二进制位模式**,称之为 "**符号位**"、"**尾数位**"、"**指数位**"。将这三部分如下拼接,即得到了该**浮点数的二进制表示**。 ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-033FEDA27CEC947A888032D8C700125E.png|282]] - **==符号位==(Sign bit)** :对 $s$ 的编码—— 直接取 $s$ 的值( 1 表示负,0 表示正) - **==指数位/阶码==(Exponent bit)**:对指数 $E$ 的编码; - **==尾数位==(Fraction / Mantissa bits/ Significand)**:对尾数 $M$ 的编码; > [!NOTE] 在 IEEE 754 标准下,浮点数的二进制表示中可划分为上述三个域:符号位、指数位、尾数位 <br> ## 32 位单精度与 64 位双精度格式 IEEE 754 标准具体规定了两种**不同位宽**下的浮点数二进制表示格式: 「**==32 位单精度格式==**」、「**==64 位双精度格式==**」 ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-537DB53920BBF9020B5CEEECD6918A19.png|698]] 在两种不同**总位宽**下,规定的"符号位"、"指数位"、"尾数位"对应的**位宽**如下: | IEEE 754 标准 | 符号位 | 指数位 | 尾数位 | | ----------- | ------- | ------------------- | ------------------ | | 32 位单精度格式 | 1 位(31) | **==8 位==**(23~30) | **==23 位==**(0~22) | | 64 位双精度格式 | 1 位(63) | **==11 位==**(52~62) | **==52 位==**(0~51) | 其中,**指数位位宽**限定了**可表示的==浮点数的区间范围==**,**尾数位位宽**限定了**可表示的==浮点数的精度位数==**。 | IEEE 754 标准 | 尾数位位宽 | 对"规格化值"的表示==精度== | 对"非规格化值"的表示==精度== | | ----------- | ----- | ------------------ | ----------------- | | 32 位单精度格式 | 23 位 | **24 位**(隐含整数位的 1) | **23 位** | | 64 位双精度格式 | 52 位 | **53 位**(隐含整数位的 1) | **52 位** | <br> ## IEEE 754 标准下表示的浮点数类型 根据 **==指数位 `exp`==** 部分的不同,**编码表示的浮点数值**分为**三类**: ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-3F56814293EA6C65682E0099BCE116D1.png|604]] 1. **==规格化值==**(Normalized Values):其二进制表示中的**阶码 $\exp =E+bias$**; 2. **==非规格化的值==**(Denormalized Values):其二进制表示中的**阶码 $\exp=1-bias$**,也即**全 0**; 3. **==特殊值==**:其二进制位表示中的**阶码全为 1**,根据尾数位的不同,划分: - **无穷大**(正无穷大 +∞,负无穷大 -∞):尾数位全 0 - **NaN**(Not a number) <br> ### 规格化值(Normalized Values) IEEE 754 标准规定,当二进制表示中**阶码 $\exp$ 即非全 0,也非全 1 时**,其表示的是 "**规范化浮点值**"。 IEEE 754 标准下 **==可被精确表示的规格化浮点值==** ,其浮点数形式为: $(-1)^S \times 1 . f \times 2^{E}$ - **阶码 $\exp$** 是**对指数 $E$ 采用==移码==进行编码的结果**:$\exp=E+bias$ - $bias=2^{k-1}-1$,$k$ 为阶码的位宽; - 32 位单精度下,阶码 $\exp$ 范围为 $[1, 254]$,指数 $E$ 范围为 $[-126, 127]$; - 64 位双精度下,阶码 $\exp$ 范围为 $[1, 2046]$,指数 $E$ 范围为 $[-1022, 1023]$; - **尾数** $M=1.f$ 大于 1 - **尾数位** $\text{frac}$ 取自**尾数 $M=1.f$ 中的 $f=f_{m-1}f_{m-2}\dots f_{1}f_{0}$ 部分** - $m$ 为尾数的精度位数; - $\text{frac}$ 最小为全 0,对应尾数 $M=1.00\dots 00$ - $\text{frac}$ 最大为全 1,对应尾数 $M=1.11\dots 11=2-\epsilon$ - 尾数位的位宽为 $m$ 时,**实际表示的==浮点数精度位数==** 为 $m + 1$(**隐含整数位的 1**) > ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-1DB7E2AC8F7E572D726539AC968F031E.png|519]] > [!faq] ❓为什么要以指数 $E$ 的移码作为阶码 $\exp$? > > 为了便于**浮点数在==二进制表示==下直接比较==数量级的大小==**。 > > **浮点数的指数 $E$ 可正可负,是有符号数**。如果阶码 $\exp$ 直接采用指数 $E$ 的补码表示,则正负指数之间存在符号位差异,不便于从补码表示上直接比较大小。 > > 采用**指数 $E$ 的移码**作为**阶码 $\exp$**,能够**通过阶码的==二进制表示==直接比较两个浮点数的==数量级大小==**。 > > ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-6CB015C4FD208497320D976E58303395.png|439]] > > ^wfptmz > [!faq] ❓对指数 $E$ 的移码的偏置是如何确定的? > > 在 IEEE 754 标准中,由于 **==阶码的位模式中的全 0 和全 1==** 分别用以表示 "**非规格化数**" 以及 "**特殊值**",因此**不能将 $k$ 位补码可表示的有符号值映射为全 0 或 1**,所以**补码采用的是 $bias=2^{k-1}-1$** (而不是标准移码中的 $bias=2^{k-1}$)[^1] ^zo6fsb #### 移码 > 移码是**对 "补码" 的编码**,旨在**将 "补码" 表示的有符号数全映射为 "无符号数" 从而便于比较大小**。 补码表示下,**正负数之间存在 "符号位" 差异**,而**不便于直接比较大小**。 移码通过**将补码表示范围内的==有符号值==加上==偏置值== $bias$** 全部映射为 **==无符号值==**,从而**便于直接比较大小**——将**补码表示中的最小负值**映射为**全 0**,将**补码表示中的最大正值**映射为**全 1**。 给定位宽 $w$ 下,标准移码的偏置值 $bias=2^{w-1}$,补码 $[X]_{补}$ 的移码表示为 $[X]_{移}=[X]_{补}+bias$。 在标准移码偏置 $bias=2^{w-1}$ 下, **$[X]_{移}$ 与 $[X]_{补}$ 仅==符号位不同==**。 <br> ### 非规范化值(Denormalized Values) IEEE 754 标准规定,当二进制表示中**阶码 $\exp$ 全 0** 时,其表示的是 "**==非规范化浮点值==**"。 IEEE 754 标准下 **==可被精确表示的非规格化浮点值==** ,其浮点数形式为: $(-1)^S \times 0 . f \times 2^{1-bias}$ - **阶码 $\exp$** 规定为**全 0** - **指数 $E$** 规定为 $E=1-bias$; - $bias=2^{k-1}-1$,$k$ 为阶码的位宽; - 32 位单精度下,指数 $E = -126$ - 64 位双精度下,指数 $E = -1022$ - **尾数 $M=0.f$ 小于 1 - **尾数位** $\text{frac}$ 取自**尾数 $M=0.f$ 中的 $f=f_{m-1}f_{m-2}\dots f_{1}f_{0}$ 部分** - $m$ 为尾数的精度位数; - $\text{frac}$ 最小为全 0,对应尾数 $M=0.00\dots 00$ - $\text{frac}$ 最大为全 1,对应尾数 $M=0.11\dots 11=1-\epsilon$ > ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-7C533F62779BDA586AF21F6E91F049FD.png|513]] <br> #### 引入非规格化值的原因 > ❓ <font color="#c0504d">为什么需要非规格化值?</font> ^5iwe2m 引入非规格化值的目的在于 "**填补绝对值意义下==最小规格数与零的距离==**",从而**避免==突然下溢==**。 以 32 位单精度为例,对于最小的规格化值为 $1.0 *2^{-126}$: - 其与**相邻的规格化值** $1.00\dots 01*2^{-126}$ 的间距为 $2^{-23}*2^{-126}=2^{-149}$, - 其与 0 的间距为 $2^{-126}$, **最小规格化值与 0 之间的距离**,是与其**相邻规格化值**的 $2^{23}$ 倍。 引入规格化值后,**最小指数值**为 $E=1-bias=-126$,而对应的**尾数部分**则有: - 规格化值的尾数 $[1.00\dots 00,\;1.11\dots 11]$ - 非规格化值的尾数 $[0.11\dots 11, \;0.00\dots 01]$ 可见,在**最小指数量级**上,"相邻两浮点数之间的距离" 以及 "最小非规划值到 0 之间的距离" 都是 $2^{-23}*2^{-126}=2^{-149}$,提供了从**最小规格化值**到**非规格化值**再到 0 的 **==平滑数值过渡==**。 > [!note] 引入**非规格化值**的优点: > > - **增加了==接近零值的表示精度==**——对于接近于 0 的极小浮点数真值,能够在损失一部分精度的情况下**能得到更近似表示,从而避免直接下溢到零**; > - **提供了==平滑的数值过渡==**——避免了在数值计算中突然跳跃到零的问题。 <br> ### 规范化值与非规范化值的区别 > ❓<font color="#c0504d">规范化值与非规范化值的区别是什么?</font> ^6hnxfx 从字面上理解: - "**规范化值**" 是**能够表示为==规范化形式== $(-1)^S \times 1 . f \times 2^{E}$ 且 $E$ 位于有效范围的浮点值** (整数位为 1,且 $E=\exp-bias$,其中 $\exp$ 范围根据阶码位宽确定) - "**非规范值**" 是**只能表示为 $(-1)^S \times 0 . f \times 2^{1-bias}$ 的值** (整数位为 0,但指数位 $E=1-bias$ 已经是最小值,无法再右移小数点) "规格化的值" 和 "非规格化的值" 都是**能被精确表示的浮点数**,但二者的覆盖区间不同。 **非规格化值表示的是接近于 0 的极小浮点数**,**位于==最小规格化值到 0 之间**==:`[0, 最小规格化值)`,如下图所示: ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-502C5C05D1EA3CF34512319D1D1B0D72.png|595]] <br><br> ## IEEE 754 标准下可表示的浮点数范围 ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-72429D781394AF001B11DC9A2C615E28.png|812]] - $2-\varepsilon$ 表示 $[1.11\dots11]_2$,即**给定位宽下小于 2 的==最大==二进制小数值** - $1-\varepsilon$ 表示 $[0.11\dots11]_2$,即**给定位宽下小于 1 的==最大==二进制小数值** > [!summary] 总结:不同值的二进制编码格式 > > - 规格化值: > - 范围: $[1 . \overbrace{00 \ldots 00}^{23 \text { bits }} \times 2^{-126}, \;1 . \overbrace{11 \ldots 11}^{23 \text { bits }} \times 2^{127}]$ ,即 $[2^{-126}, 2^{127}]$ > - **最小规格化值**表示为: $[\;(s)(00\dots...01)(00\dots...000)]$ ,指数位**为 1**,尾数位**全 0** > - **最大规格化值**表示为: $[\;(s)(11\dots...10)(11\dots...111)]$ ,指数位**最低位 0 其余全 1**,尾数位**全 1** > > > > - 非规格化值: > - 范围: $[0 . \overbrace{00 \ldots 01}^{23 \text { bits }} \times 2^{-126}, \;0 . \overbrace{11 \ldots 11}^{23 \text { bits }} \times 2^{-126}]$ ,即 $[2^{-149}, 2^{-126})$, 共 $2^{23}-1$ 个。 > - **最小非规格化值**表示为: $[\;(s)(00\dots...00)(00\dots...001)]$ ,指数位**全 0**,尾数位**为 1** > - **最大非规格化值**表示为: $[\;(s)(00\dots...00)(11\dots...111)]$ ,指数位**全 0**,尾数位**全 1** > > > > - **±1** 表示为: $[\;(s)(01\dots...11)(00\dots...000)]$,指数位最高位 0 其余全 1,尾数位全 0 > > > > - **±∞** 表示为:$[\;(s)(11\dots...11)(00\dots...000)]$,指数为全 1,尾数位全 0 <br><br><br> # IEEE 754 标准下对浮点数的编码过程 > [!important] > IEEE 754 标准规定的二进制格式下,可表示的浮点数是 **==精度有限==**、**==非均匀分布==** 的, > 其中 **指数位位宽**限定了**可表示的==浮点数的区间范围==**,**尾数位位宽**限定了**可表示的==浮点数的精度位数==**。 > > 对于**==不能被精确表示的浮点数==**,会根据一定原则进行 "**==舍入==**(rouding)"——舍入为**能够被精确表示的浮点数**。 ### 过程概述 IEEE 754 标准对**浮点数**进行**二进制编码**的过程: ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-EA1E64219CAC33A1B1E57365CADF6CDB.png|639]] 1. 将**二进制浮点数**表示为**规定的标准形式** $V=(-1)^s\times M\times 2^E$ 2. **对浮点数标准形式中的 $s$、$M$、$E$ 分别进行编码**,得到对应的 **==二进制表示==中的符号位 $S$、阶码 $\exp$、尾数位 $\text{frac}$**: - 符号位 $S$ 是对 $s$ 的编码—— $S$ 直接取 $s$ 的值; - 阶码 $\exp$ 是对指数 $E$ 的**编码**; - 尾数位 $\text{frac}$ 是对尾数 $M$ 的**编码**; ### 具体过程 >❓<font color="#c0504d">给定一个十进制浮点数,如何得到其在 IEEE 754 标准下的二进制表示?</font> ^l5xzib **十进制浮点数转 IEEE 754 二进制表示**的具体过程: - (1)将**十进制浮点数**转换给**二进制浮点数**表示:$b_mb_{m-1}\cdots b_{1}b_{0}\;.b_{-1}b_{-2}\cdots b_{-n-1}b_{-n}$ <br> - 将**整数部分**、**小数部分**分别转换为二进制形式; - 整数部分,除 2 取余,逆序排列余数得到; - 小数部分,乘 2 取整,顺序排列整数得到; - 32 位单精度下,对规格化值最多保留 **24 位有效数字**,对非规格化值保留 **23 位有效数字** - 64 位双精度下,对规格化值最多保留 **53 位有效数字**,对非规格化值保留 **52 位有效数字 - (2) 尝试对**二进制浮点数** $V$ 进行 **==规范化==** 得到尾数 $M$ 和 $E$ <br> - 若二进制小数能在**左移 $E=bias$ 位、右移 $E=1-bias$ 位以内**(即 $E\in[1-bias,bias]$)<br>得到**规范化形式** $(-1)^S \times 1 . f \times 2^{E}$,其中则其为 **==规范化值==**。<br> - 若二进制小数在**右移 $E=1-bias$ 位**后,其**整数部分仍然为 0**,<br>即为**非规范化形式** $(-1)^S \times 0.f \times 2^{1-bias}$ 则其为 "**==非规范化值==**"。 <br> - 若二进制小数在**左移 $E=bias$ 位**后,其整数部分仍大于 1: - 若小数部分全 0,则其表示为 "**==无穷==**"; - 否则,表示为 "==NaN==" - (3)取得**阶码 $\exp$**:<br> - 对于**规范化值**,采用 "**移码**" 对指数 $E$ 进行编码得到**阶码** $\exp=E+bias$; - 对于**非规范化值**,**阶码 $\exp=0$; - 对于**特殊值**,**阶码 $\exp$ 全 1** ; - (4)取得**尾数位 $\text{frac}$** <br> - 取尾数 $M$ 中的小数部分作为尾数位,即 $\text{frac}=f_{m-1}f_{m-2}\dots f_{1}f_{0}$。 - 对于**规范化值**: $M={1.f_{m-1}f_{m-2}\dots f_{1}f_{0}}$ ; - 对于**非规范化值**: $M={0.f_{m-1}f_{m-2}\dots f_{1}f_{0}}$; - (5)将符号位、阶码、尾数位**三段拼接**,得到 $[(s)(exp)(f)]_2$ 的形式即为**该浮点数的二进制表示** ### 示例 > [!example] > > 示例一:对十进制小数 $25.125_{10}$ 采用 32 位单精度编码表示: > > ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-4F998FA3724E938638E75AC246E9EE1A.png|700]] > >示例二: 对十进制小数 $8.5_{10}$ 采用 32 位单精度编码表示: > > ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-E2957C38F013D3D1987809173B6A46A2.png|375]] > > 示例三:对十进制小数 $-6.625$ 采用 32 位单精度编码表示: > > ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-B4BB911E52D9CC5C03855C2D0414850E.png|553]] > <br><br><br> # 舍入 在 IEEE 754 标准下,如果一个**浮点数不能被精确表示**,会采用与其**近似的可表示值**对其进行表示, 这个过程称为 **==舍入==(rounding)**。 IEEE 754 标准定义了几种不同的舍入模式: - 向上舍入(Round Toward Positive):总是向正无穷方向舍入。 - 向下舍入(Round Toward Negative):总是向负无穷方向舍入。 - 向零舍入(Round Toward Zero):总是向零的方向舍入。 - **==最近偶数舍入==**(Round to Nearest, Ties to Even),包含**两点规则**: - 如果数字**距离两个可能值的距离不同**,则 **==就近舍入==**(**向上或向下舍入**,无论奇偶),**不涉及选择**。 - 如果数字**正好位于两个可能值的正中间**,则 **==舍入到最近的偶数==**(让尾数部分最低有效位为 0) - 在二进制小数下,这种情况只会是 $XX\cdots XX.YYY100\cdots0$ 的形式,其中**最右位的 $Y$ 为精确数位**,<br>即**精确位的后一位为 1,其后全 0 的情况**,将舍入到最近的偶数。 其中,**默认方式**是"**==最近偶数舍入==**"。 > [!example] "==最近偶数舍入==" 示例 > > 精确到**小数点后的第二位**,即**四分之一位**。 > > - $10.00011_2$ 向下最近舍入为 $10.00_2$; (相较于 $10.01$,更接近 $10.00$,因此向下舍入) > - $10.00110_2$ 向上最近舍入到 $10.01_2$; (相较于 $10.00$,更接近 $10.01$,因此向上舍入) > - $10.11100_2$ 向偶数舍入到 $11.00_2$ (正好位于 $11.00$ 和 $10.11$ 的中间,向着让最低有效位为 0 一侧舍入) > - $10.10100_2$ 向偶数舍入到 $10.10_2$ (正好位于 $10.10$ 和 $10.11$ 的中间,向着让最低有效位为 0 一侧舍入) > - $1.111_2$ 向偶数舍入到 $10.00_2$(正好位于 $10.00$ 和 $1.11$ 的中间,向着让最低有效位为 0 一侧舍入) > > > > > - $0.1001_2$ 向下最近舍入到 $0.10_2$ > - $0.1010_2$ 向偶数舍入到 $0.10_2$ > - $0.1011_2$ 向上最近舍入到 $0.11_2$ > - $0.1101_2$ 向下最近舍入到 $0.11_2$ > - $0.1110_2$ 向偶数舍入到 $1.00_2$ > - $0.1111_2$ 向上最近舍入到 $1.00_2$ > <br><br><br> # 浮点运算 在 IEEE 754 的表示下,两个浮点数真值 $x$ 与 $y$ 之间的任何运算 $\odot$ 的运算结果均为 "近似"值 **$\text { Round }(x \odot y)$**, 即对**实际运算的精确结果进行==舍入==后的结果**。 ##### 浮点加法 浮点加法:$x +^f y = \text{Round}(x + y)$ : - 满足交换律: $x +^f y=y +^f x$ - **==不满足结合律==**:$(x +^f y)\; +^f\; z \ne x +^f\;(y +^f\; z)$ ,因为改变运算顺序后**可能导致舍入结果不同** - 例如 $(3.14+1e10)-1e10=0.0$(因为舍入),而 $3.14+(1e10-1e10)=3.14$ - $x +^f\; (-x)=0$ - $\text{NaN} +^f\; x=\text{NaN}$ - $+\infty-\infty=\text{NaN}$ ##### 浮点乘法 浮点乘法: $x *^f y = \text{Round}(x * y)$ ##### 浮点除法 - 定义了 $\large \frac{1}{0}=\infty$, $\large \frac{1}{-0}=-\infty$ 。 <br><br><br> # 浮点数比较 ##### C++中的浮点数比较 > 受精度影响,不能直接用 `==` 判断等于。 - 判断相等:`fabs(a - b) < eps` - 判断 lt;$ :`a < b - eps` - 判断 $\leq$ :`a < b + eps` - 判断 gt;$ :`a > b + eps` - 判断 $\geq$ :`a > b - eps` `eps` 通常取 `1e-9` 或 `1e-10`。 <br><br><br> # 常见的浮点数错误 ## 浮点数判断相等 > [!info] IEEE 754 二进制表示下**判断两个浮点数**相等:通过比较**二者差值的绝对值是否在给定精度范围内** > > ```cpp > // 判断两个浮点数相等 > // 通过比较二者差值的绝对值是否在给定精度范围内来近似判定相等 > // `std::fabs()`位于`<cmath>`头文件内 > bool FloatsEqual (float a, float b, float epsilon = 0.00001) { > return std:: fabs (a - b) < epsilon; > } > ``` ##### 错误判断 > [!error] 不能对两个浮点数值直接用 `==` 判断 $0.1_{10}=0.0\underbrace{0011}_{\text{无限循环}}0011\dots(B)$ $0.2_{10}=0.\underbrace{0011}_{\text{无限循环}}0011\dots(B)$ $0.3_{10}=0.0\underbrace{1001}_{\text{无限循环}}1001\dots(B)$ $0.4_{10}=0.\underbrace{0110}_{\text{无限循环}}0110\dots(B)$ 如上所示,$0.1$ 和 $0.2$ **在二进制表示下都是无限循环小数**,因此实际存储时会进行 "舍入" 而**只保留 24 位(32 位下)或 54 位(64 位下)精度**,因此在计算机中(IEEE 754 格式下) - $0.1$ 会在舍入后被实际存储为 $0.10000000000000001$(大约) - $0.2$ 会在舍入后被实际存储为 $0.20000000000000001$(大约) - $0.3$ 会在舍入后被实际存储为 $0.29999999999999999$(大约) - 在计算机中 `0.1 + 0.2` 会得到结果 $0.1+0.2=>0.30000000000000004$(大约) 因此,`0.1 + 0.2 != 0.3 ` 始终成立。 ![[_attachment/02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示.assets/IMG-浮点数的表示-045E7F40C948AFB3487D3B0727281567.png|710]] <br><br><br> # 参考资料 [南京大学《计算机系统基础(一):程序的表示、转换与链接》中国大学慕课,袁春风老师](https://www.icourse163.org/learn/NJU-1001625001?tid=1472100484#/learn/content?type=detail&id=1257458477&sm=1) # Footnotes [^1]: [移码 - 维基百科,自由的百科全书](https://zh.wikipedia.org/wiki/%E7%A7%BB%E7%A0%81)