0%

半精度/单精度浮点数转化

IEEE754浮点数规范复习

在IEEE754中,不同精度的浮点数各部分位宽如下表所示

格式 总位数 阶码位数 尾数位数
半精度 16 5 10
单精度 32 8 23
双精度 64 11 52
四倍精度 128 15 112

规格化数和非规格化数

  • 规格化数

    阶码不允许出现全0或者全1的情况,指数在阶码中的编码形式为偏移码,偏移量为\(2^{k - 1} - 1\)

  • 非规格化数

    • 阶码全1
      • 尾数全0,表示∞
      • 尾数非0,表示NaN(Not a number)
    • 阶码全0
      • 尾数全0,表示浮点数0
      • 尾数非0,指数仍然为\(1 - offset\),底数表示\(0.M\),注意此时不同于规格化数默认小数点左侧为1

C语言转换实现

该方法中需要注意的是非规格化数0.···的转化,该部分代码将fraction部分的编码持续左移,直到最高位1超出10位的位宽,其目的是重新构造规格化表示的小数,所以可以看到exponent也在同步递减来保持原来的指数

我们知道在阶码全0的非规格化数中存在一种‘合法’表示的浮点数,该浮点数的小数部分前不补1,同时阶码部分仍视为\(1 - offset\)(原因是用于和最小的规格化浮点数平滑的连接)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
// 此处如果是MacOS,稍微别名一下类型
#ifdef __APPLE__
typedef u_int16_t uint16_t;
typedef u_int32_t uint32_t;
#endif

float convert_Float16_to_float(_Float16 val) {
uint16_t fp16 = *(uint16_t*)&val;
uint32_t sign = (fp16 & 0x8000) << 16;
uint32_t exponent = (fp16 & 0x7C00) >> 10;
uint32_t fraction = (fp16 & 0x03FF);
// 非规格化数处理
if (exponent == 0) {
if (fraction == 0) { // 0
return *(float*)&sign;
}
// 0.···数规格化
exponent = 1;
while ((fraction & 0x0400) == 0) {
fraction <<= 1;
exponent -= 1;
}
fraction &= ~0x0400;
} else if (exponent == 0x001F) {
if (fraction == 0) { // ∞
sign |= 0x7F800000;
return *(float*)&sign;
}
// NaN
sign |= (0x7F800000 | fraction << 13);
return *(float*)&sign;
}
exponent = exponent - 15 + 127;
fraction <<= 13;
sign |= (exponent << 23 | fraction);
return *(float*)&sign;
}

int main() {
_Float16 val16 = 2;
float val32 = convert_Float16_to_float(val16);
printf("%f\n", val32);
return 0;
}

关于C语言强制类型转换的思考

C语言的强制类型转换在两方面存在差异:整型之间的转化、整型和浮点数之间的转换

有符号整数/无符号整数的转换

在整型有无符号的转换之间,编译器并不会改变变量的底层编码,仅改变编码的解读方式

1
2
3
4
int main() {
int x = -1;
printf("unsigned x = %u, signed x = %d\n", (unsigned)x, x);
}

输出unsigned x = 4294967295, signed x = -1,这两个数底层编码相同。但情况在浮点数/整数之间不同

整型/浮点数的转换

如果将整数/浮点数之间相互转换,C语言编译器会改变相应变量的底层编码,即补码IEEE754浮点数两种编码方式之间的转换。同时需要注意的是C语言中不支持浮点数位运算,因此在上述示例代码中我采用指针的强制类型转换形式来保留编码格式