程序员数学之-IEEE754规范

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

float16

单精度浮点 float32

双精度浮点float64

2.2 规格数、非规格数与特殊数

根据阶码E区分,IEEE754规范规定了三种状态:

  1. normal number(规格数)

    特点是:阶码E不为0和且不为全1

    以float32为例,其形式为:

    其值的大小为:

    对于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)

    可见与我们想法一致:

  2. subnormal number(非规格数)

    特点是:阶码E=0, 表示0以及0附近的值)

    其值的大小为(注意,标准规定非规格数指数为1-bias),无论尾数M为多少,都是一个比较接近0的值:

    同样的:对于float16(bias = 15),float32(bias = 127),float64(bias = 1023)。

  3. 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次幂:

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;
}

参考:

  1. IEEE754规范: 四, 非规格数, ±infinity, NaN - 知乎 (zhihu.com)
  2. 快速浮点数exp算法_快速exp-CSDN博客
posted @ 2024-01-21 19:18  sureZ_ok  阅读(110)  评论(0编辑  收藏  举报