浮点数表示方法

浮点数是用科学计数法来表示一个数字的,它的表示格式可以写成这样:

这种表示格式根据每个部分中不同的数据分成这四种类型:

IEEE754标准中的四种舍入模式

Round-to-even/Round-to-nearest(向偶数舍入/就近舍入)

1
2
3
4
对于1.001_1001,舍入处理后为1.010(去掉多余的4位,加0.001
对于1.001_0111,舍入处理后为1.001(去掉多余的4位)
对于-1.001_1000,舍入处理后为-1.010(去掉多余的4位,加0.001,因为此时最低位为1
对于-1.010_1000,舍入处理后为-1.010(直接去掉多余的4位,因为此时最低位为0

所以对于需要取近似值时,需要查看舍去数据的高两位和舍去数据的上一位

下面是将一个uint32_t的数字按照float格式转化为float,调试打印每个部分

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
uint32_t uint2float(uint32_t u)
{
// 32 zeros
if (u == 0x0)
{
return 0x0;
}
uint32_t n = 31;
uint32_t e, f;
uint32_t s = u >> 31;
u &= ((1u << 31) - 1);
std::cout << std::hex << u << std::endl;
while (n >= 0 && (((u >> n) & 0x1) == 0x0))
{
n = n - 1;
}

std::cout << std::hex << "n: " << n << std::endl;
// <= 0 000 0000 1. 111 1111 1111 1111 1111 1111
if (u <= 0x00ffffff)
{
// no need round
uint32_t mask = 0xffffffff >> (32 - n);
f = (u & mask) << (23 - n);
e = n + 127;
return (s << 31) | (e << 23) | f;
}
else
{
// need round
// expand to 64 bit for situations like 0xffffffff
uint64_t u_tmp = u;

// get low n-23 bits
uint32_t extra_num = u_tmp & ((1 << (n - 23 - 1)) - 1);
uint32_t ret_num_lowest_bit = (u_tmp & (1 << (n - 23 - 1))) >> (n - 23 - 1);
std::cout << std::hex << "extra_num: [" << extra_num << "] ret_num_lowest_bit: [" << ret_num_lowest_bit << "]" << std::endl;
if (extra_num < (0x1 << (n - 23 - 1 - 1)))
{
f = u >> (n - 23 - 1);
e = n + 127;
}
else if (extra_num == (0x1 << (n - 23 - 1 - 1)))
{
if (ret_num_lowest_bit == 0x0)
{
f = u >> (n - 23 - 1);
e = n + 127;
}
else
{
f = (u >> (n - 23 - 1)) + 1;
e = n + 127;
}
}
else
{
f = (u >> (n - 23 - 1)) + 1;
e = n + 127;
}
std::cout << std::hex << "f: [" << f << "] e: [" << e << "]" << std::endl;
return (s << 31) | (e << 23) | f;
}
// inf as default
return 0x7f800000;
}

假如输入的数字为0x10000018则

观察调试信息

f为23位尾数,向偶数舍入,最后0x8进1变成0x1000002,ret_num_lowest_bit就是用来判断是否进位,如果为1就是奇数,舍去后需要进1,如果为0是偶数,不需要进1

Round-toward-zero(向0舍入,正数向小舍入,负数向大舍入)

Round-down(向小舍入)

Round-up(向大舍入)

16位有符号浮点数可以表示成这样

由1位的符号位S(sign)、5位的指数E(exponent)和10位的尾数M(mantissa)

其中的每个部分计算公式

根据公式可以这样定义float_16:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
struct Ufloat16Bit
{
union
{
struct
{
uint16_t sign : 1;
uint16_t exponent : 5;
uint16_t mantissa : 10;
};
uint16_t raw;
};
Ufloat16Bit(uint16_t raw) : raw(raw) {}

static from_fields(uint8_t sign, uint8_t exponent, uint16_t mantissa)
{
Ufloat16Bit ufloat;
ufloat.sign = sign & 0x1;
ufloat.exponent = exponent & 0x1f;
ufloat.mantissa = mantissa & 0x3ff;
return ufloat.raw;
}

template <typename T>
static Ufloat16Bit from_value(T num)
{
static_assert(std::is_integral<T>::value, "T must be an integer type");
return Ufloat16Bit(static_cast<uint16_t>(num));
}

// 转换为 float32
float to_float32() const
{
if (exponent == 0)
{
if (mantissa == 0)
{
return 0.0f;
}
else
{
return static_cast<float>(mantissa) * std::pow(-1, mantissa) * std::pow(2.0f, -14.0f) / 32.0f;
}
}
else if (exponent < 31)
{
return std::pow(-1, mantissa) * std::pow(2.0f, static_cast<float>(exponent) - 15.0f) * (1.0f + static_cast<float>(mantissa) / 32.0f);
}
else
{ // e == 31
if (mantissa == 0)
{
return INFINITY;
}
else
{
return NAN;
}
}
}
}

11位无符号浮点数可以表示成这样

11位无符号浮点数无符号位,由5位的指数E(exponent)和6位的尾数M(mantissa)

其中每个部分计算公式

根据公式可以这样定义这个类型:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

struct Ufloat11Bit
{
union
{
struct
{
uint16_t exponent : 5;
uint16_t mantissa : 6;
};
uint16_t raw;
};
Ufloat11Bit(uint16_t raw) : raw(raw) {}

static from_fields(uint8_t sign, uint8_t exponent, uint16_t mantissa)
{
Ufloat11Bit ufloat;
ufloat.exponent = exponent & 0x1f;
ufloat.mantissa = mantissa & 0x3ff;
return ufloat.raw;
}

template <typename T>
static Ufloat10Bit from_value(T num)
{
static_assert(std::is_integral<T>::value, "T must be an integer type");
return Ufloat10Bit(static_cast<uint16_t>(num));
}

// 转换为 float32
float to_float32() const
{
if (exponent == 0)
{
if (mantissa == 0)
{
return 0.0f;
}
else
{
return static_cast<float>(mantissa) * std::pow(-1, mantissa) * std::pow(2.0f, -14.0f) / 32.0f;
}
}
else if (exponent < 31)
{
return std::pow(-1, mantissa) * std::pow(2.0f, static_cast<float>(exponent) - 15.0f) * (1.0f + static_cast<float>(mantissa) / 32.0f);
}
else
{ // e == 31
if (mantissa == 0)
{
return INFINITY;
}
else
{
return NAN;
}
}
}
}

10位无符号浮点数可以表示成这样

10位无符号浮点数无符号位,由5位的指数E(exponent)和5位的尾数M(mantissa)

其中每个部分计算公式

同理10位无符号float和11位无符号float相比就是mantissa少一位,其他相同

参考资料

IEEE754标准中的4种舍入模式