块浮点数(Block Floating-Point)-背景故事
32位固定点向量由int32_t尾数值的数组表示,同时伴随一个隐含的或(常量)显式的指数。
32位BFP(Block Floating-Point)向量由int32_t尾数和指数的数组表示,然而,指数必须是显式的,并且(通常)是动态的。此外,BFP向量还有 另一个属性,称为它们的“头空间(headroom)”,它与尾数和指数一起被跟踪。
请注意,实际上并没有“块浮点”标量。这里的“块”意味着与单个指数相关联的尾数向量。只包含1个元素的BFP向量应被视为浮点值(尽管非标准)。
头空间(Headroom)
头空间是一个适用于整数标量和向量的概念。整数标量的头空间是“值在左移不丢失信息的位数”。
以uint32_t值23为例。
uint32_t x = 23;
作为一个无符号的32位 整数,表示数字23的位模式为:
0000 0000 0000 0000 0000 0000 0001 0111
第一个非零位之前有27个0。
那么,如果我们执行以下操作会发生什么(假设>>不是算术右移)?
printf("x: %u\n", ((x<<27)>>27) );
经过27位左移后,我们得到:
1011 1000 0000 0000 0000 0000 0000 0000
然后进行27位右移,我们回到了起始位置:
0000 0000 0000 0000 0000 0000 0001 0111
所以printf()输出:
x: 23
那么这个呢?
printf("x: %u\n", ((x<<28)>>28) );
经过28位左移后,我们得到:
0111 0000 0000 0000 0000 0000 0000 0000
1丢失了!现在进行右移后,我们得到:
0000 0000 0000 0000 0000 0000 0000 0111
所以printf()输出:
x: 7
左移28位导致信息丢失,而27位左移没有丢失信息。无符号32位整数x的头空间是27位,即它可以左移的位数而不丢失信息。
等价地,无符号整数的头空间(有时称为“无符号头空间”)可以定义为“前导零的位数”。我们左移掉的每个零在右移时都会被替换为零。
那么对于有符号整数呢?
我们同时考虑int32_t值23和-23的对:
int32_t x = 23;
int32_t y = -23;
它们的二进制补码表示为:
0000 0000 0000 0000 0000 0000 0001 0111 // x 1111 1111 1111 1111 1111 1111 1110 1001 // y
这次,让我们假设>>是一次算术右移:
printf("x: %d\n", ((x<<27)>>27) );
printf("y: %d\n", ((y<<27)>>27) );
输出结果是什么?
经过左移操作:
1011 1000 0000 0000 0000 0000 0000 0000 // x 0100 1000 0000 0000 0000 0000 0000 0000 // y
经过算术右移:
1111 1111 1111 1111 1111 1111 1111 0111 // x 0000 0000 0000 0000 0000 0000 0000 1001 // y
所以printf()输出:
x: -9 y: 9
27位左移破坏了我们的值!
那这个呢?
printf("x: %d\n", ((x<<26)>>26) );
printf("y: %d\n", ((y<<26)>>26) );
经过左移操作:
0101 1100 0000 0000 0000 0000 0000 0000 // x 1010 0100 0000 0000 0000 0000 0000 0000 // y
经过算术右移:
0000 0000 0000 0000 0000 0000 0001 0111 // x 1111 1111 1111 1111 1111 1111 1110 1001 // y
所以printf()输出:
x: 23 y: -23
这次左移27位导致信息丢失,而左移26位没有丢失。