%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 ![[02-开发笔记/03-计算机基础/数据的编码表示/浮点数与有符号数之间的转换#^cnhslo]] #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later %% # 浮点数与整数之间的类型转换 > ❓<font color="#c0504d">相同位宽的浮点数类型与有符号整数类型之间如何相互转换</font>? ^cnhslo <br> ## 浮点型转整型 - 如果**浮点型的值大于整型的范围**,转为整型后将导致**溢出** - 浮点型转为整型,**小数部分会被全部舍弃**——**==向 0 舍入==** > [!caution] 浮点型与整型之间的转换由==特定的机器指令==完成,是 "**==向 0 舍入取整==**",而不是对相同位模式做不同解释!! <br> ## 32 位单精度浮点数 `float` 转 32 位有符号整型 `int` `int` 允许的数值范围是 $[-2^{31},\;2^{31}-1]$,因此: - 能转 `int` 的**最大正浮点数值**为 $1.11\dots 11 *2^{30}$,转为`int` 后**高 24 位全 1**; - 能转 `int` 的**最小负浮点数值**为 $-1.0*2^{31}$,转为 `int` 后即为 $-2^{31}$。 因此,给定 32 位单精度浮点数的 **==二进制表示==**,需要**提取出三个部分**: - 符号位 `sign = (x >> 31) & 1` - 阶码 `exp = (x >> 23) & 0xff`,则指数 `E = exp - 127` - 尾数位 `frac = x & ((1<<23) - 1)` 然后**分类讨论**: - 若指数 $E = \exp-127<0$ ,则**浮点数小于 1**,转为 `int` 后为下溢为 0。<br>其中包括: - 非规格化值(**包括 0**) - 规格化值中,指数 $E=\exp-bias\ < 0$ 的 - 若阶码 $\exp==255$ 或者指数 $E>31$,表示为**特殊值或转为`int` 后将溢出**; - 若指数 $E==31$ 且符号位为 1 且尾数位 0,即 `E == 31 && sign && !frac`,则恰可转为**最小负整数**; - 若指数 $0\leq E\leq 30$,则为规格化值: - 需要在`frac` 的最高位前补上隐含的 1:`frac |= (1 << 23)`; - 若 `E > 23`,则需要左移 `frac <<= (E - 23)`,得到**整数位**; - 若 `E < 23`,则需要右移 `frac >>= (23 - E)`,将小数部分通过右移来消除掉,**只保留整数部分**; - 最后,若为正数,则返回 `frac`;若为负数,则返回 `-frac`(取反 + 1) ##### 代码实现 给定 32 位单精度浮点数的二进制表示 `uf`,返回该**浮点数值**转换为 `int` 类型后对应的**整数值**。 ```cpp // 实现 (int) f. // 给定32位单精度浮点数的二进制表示, 将该浮点数值转换为int. int floatFloat2Int(unsigned uf) { // 24 op int sign = uf & (1 << 31); int exp = (uf >> 23) & 0xff; int frac_mask = (1 << 23) - 1; // 0x7fffffff int frac = uf & frac_mask; int E = exp - 127; if (E < 0) { // less than 1, then underflow to 0. return 0; } else if (exp == 0xff || E > 31) { // overflow. return 0x80000000u; // INTMIN } else if (E == 31 && sign && !frac) { // -1.0 * 2^31 return 0x80000000u; } else { // `E<=30` frac |= 1 << 23; // add the hidden bit if (E > 23) { frac <<= (E - 23); } else { frac >>= (23 - E); } return sign ? -frac : frac; } } ``` <br><br> ## 32 位有符号整型 `int` 转 32 位单精度浮点数类型 `float` > [!summary] > 32 位有符号整型 `int` 范围内的整数: > > - 只有那些 **==有效二进制数值位==在==去掉后缀 0== 后剩余不超过 24 位的整型值**才能够被 **32 位单精度浮点类型 `float`** 精确表示 > - (因为 **`float` 尾数位为 23 位**,对于规格化值**只具有 24 位有效精度**) > > > > > - 范围内的 **==所有整数值==** 都能被 **64 位双精度浮点数类型 `double`** 精确表示。 > - (因为 **`double` 尾数位为 52 位**,对于规格化值**具有 53 位有效精度**,能够完全覆盖 `int` 类型的 31 位有效数值位。 有符号整型 `int` 32 位,其中 $31$ 位数值位,都是有效数字位。 32 位单精度浮点数类型 `float` 中**尾数位为 23 位**,对于规格化值最多**具有 24 位精度**。 因此,**将 `int` 类型转为 `float` 类型**的过程中,如果**原整数的==有效二进制位超过 23 位==**,则会发生 **==舍入==**(按[[02-开发笔记/03-计算机基础/数据的编码表示/浮点数的表示#舍入|最近偶数舍入]]),**只保留 23 位**,由此会发生精度丢失,导致从整数类型转为浮点数类型后,**==原整数值将会被改变==**。 例如,根据下方代码,将下列有效二进制位超过 23 位的整型数转换 float 后,将会**发生精度丢失**,得到结果如下表: ```cpp void int_2_float(int a) { float f = a; int b = f; } ``` | 原整数值 $a$(对应 0x 表示) | 转为 `float` 类型后的浮点值 $f$ | 将 `float` 类型再次转为 `int` 类型后的值(对应 0x 表示) | | -------------------------------- | ---------------------- | -------------------------------------- | | $33554431$($\text{0x1FFFFFF}$ ) | $33554432.000000$ | $33554432$($\text{0x2000000}$) | | $33554428$($\text{0x1FFFFFD}$ ) | $33554428.000000$ | $33554428$($\text{0x1FFFFFC}$) | | $536870879$($\text{0x1FFFFFDF}$) | $536870880.000000$ | $536870880$($\text{0x1FFFFFE0}$) | | $536870864$($\text{0x1FFFFFD0}$) | $536870848.000000$ | $536870848$($\text{0x1FFFFFC0}$) | | $536870865$($\text{0x1FFFFFD1}$) | $536870880.000000$ | $536870880$($\text{0x1FFFFFE0}$) | | $536870800$($\text{0x1FFFFF90}$) | $536870784.000000$ | $536870784$($\text{0x1FFFFF80}$) | | $536870801$($\text{0x1FFFFF91}$) | $536870816.000000$ | $536870816$($\text{0x1FFFFFA0}$) | | $536870799$($\text{0x1FFFFF8F}$) | $536870784.000000$ | $536870784$($\text{0x1FFFFF80}$) | <br><br> # 参考资料 # Footnotes