1 定点数与浮点数
在现实生活中,不仅要有整数,还需要小数,计算机怎么表示小数呢?有两种方式:定点数与浮点数
定点数(Fixed Point Number):
顾名思义,小数点位置固定,例如常见的Qm.n 表示法,共需1(符号位)+ m (整数位) + n (小数位)bit位来表示数据,如Q7,Q15,Q31 等数据类型。
其优点是:计算速度快;
缺点是:表示范围小,不利于同时表达特别大或者特别小的数。
浮点数(Floating Point Number):
使用科学计数法表示一个小数,优点是表示范围广、精度较高,但计算速度相对复杂。
2 IEEE754规范
2.1 浮点存储格式
IEEE754规范规定了float16、float32(即float)、float64(即double)其存储格式如下,分为三部分表示:符号位(S,蓝色块),阶码(E,绿色块),尾数(M,红色块):
半精度浮点 float16
单精度浮点 float32
双精度浮点float64
2.2 规格数、非规格数与特殊数
根据阶码E区分,IEEE754规范规定了三种状态:
-
normal number(规格数)
特点是:阶码E不为0和且不为全1
以float32为例,其形式为:
其值的大小为:
$$
V=(-1)s\times(1.M)\times2
$$
对于float16(bias = 15),float32(bias = 127),float64(bias = 1023)。
规格数不能表示0和非常靠近0的数。
例如:对于一个float32:-3.456 该怎么表示呢?
符号位:为-1,所以s=1,3.456=(1 + 0.728 ) * 2,所以 M = 0.728 E = 1 + 127 = 128
使用工具验算 IEEE-754 Floating Point Converter (h-schmidt.net)
可见与我们想法一致:
-
subnormal number(非规格数)
特点是:阶码E=0, 表示0以及0附近的值)
其值的大小为(注意,标准规定非规格数指数为1-bias),无论尾数M为多少,都是一个比较接近0的值:
$$
V=(-1)s\times(0.M)\times2
$$ -
non-number(特殊数)
特点是:阶码E为全1,包括正负Inf,非数值NAN)
无穷大Inf:
非数值 NAN(Not A Number):
这几种状态在数轴上的分布如下:
2.3 浮点数表示范围与精度
范围 | 精度 | |
---|---|---|
float16 | (-2 * 2^15, 2 * 2^15) 也即:(-65536,65536) | 1/(2^10) 即 3-4位有效数字 |
float32 | (-2 * 2^127, 2 * 2^127)也即:(-3.4e+38, 3.4e+38) | 1/(2^23) 即 6~7位有效数字 |
float64 | (-2 * 2^1023, 2 * 2^1023)也即:(-1.79e+308, 1.79e+308) | 1/(2^52) 即 15~16位有效数字 |
3 浮点数之间相互转换
以float16与float32的相互转换为例:
有时需要将浮点数打印输出,%f可打印单精度浮点型数据(float),%lf可打印双精度浮点型数据(double)。
在C语言中,打印半精度浮点数(float16
)可能会有一些挑战,因为C语言标准库通常不直接支持这种类型,这时需要将其强制转换为float32打印,代码中我们可以类似如下操作:
float16_t val = 1.5;
printf("val = %f\r\n", (float32_t)val); // 将float16_t 强制转换为float32_t,通过%f打印
强转看起来比较简单,但实际上在这个过程中,float16 的符号位、指数位和尾数位将按照浮点数格式的规则进行扩展,类似如下操作:
float32_t f16Tof32(float16_t halfFloat) {
union {
uint32_t Uint32;
float32_t F32;
} val;
val.Uint32 = *((uint32_t *)(&halfFloat));
// S + E + M
val.Uint32 = ((val.Uint32 & 0x8000) << 16) |
(((((val.Uint32 >> 10) & 0x1f) - 15 + 127) & 0xff) << 23) |
((val.Uint32 & 0x03FF) << 13);
return val.F32;
}
4 快速计算2的指数次幂的原理
快速计算2的指数幂,正是利用IEEE754的浮点结构,如计算2的x次幂:
$$
2x=(-1)0\times(1.0)\times2^{((x + 127)-127)}
$$
s = 0, M = 0, E = x+127
其转换代码如下:
uint32_t power2(uint32_t x) {
union {
uint32_t Uint32;
float_t F32;
} val;
val.Uint32 = (127 + x) << 23;
return (uint32_t)val.F32;
}
// 同样的,我们也可以快速的计算log2
uint32_t log2(float_t y) {
union {
uint32_t Uint32;
float_t F32;
} val;
val.F32 = y;
return ((val.Uint32) >> 23) - 127;
}
参考:
标签:float16,val,浮点数,IEEE754,程序员,数学,127,float32 From: https://www.cnblogs.com/sureZ-learning/p/17978194