%%
# 纲要
> 主干纲要、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