浮点数在内存中的存储方式

         浮点数可以简单理解带小数点的数,像10.0也为浮点数。C/C++中使用的浮点数类型有float 和double (也分别称之为 单精度和双精度浮点数),其中float占4byte,32bit;double占8byte。在AI计算领域,又有FP32、FP16和BF16的数据类型(也称之为 单精度/半精度/混合精度 浮点数),FP32类型同C/C++中的float存储方式一样;从字面意思就可以看出,FP16和BF16类型在内存中占16bit。为了兼容和移植方便,目前的处理器都会遵循IEEE规定的存储方式。

1. 十进制转二进制浮点数

        例如,对于一个浮点数10.625,其整数部分和小数部分分别转换成二进制,整数部分除2取余,小数部分乘2取整。转换为二进制1010.101,可以校验一下小数部分:1*(2^-1) + 0 *(2^-2) +1*(2^-3) =0.5 + 0.125 = 0.625。

       对于0.625,正好可以用3bit二进制数来表示,但对于像0.1这样十进制的小数,就不能用 有限bit的二进制数来表示,如果我们用乘2取整的方法来计算,会发现结果永远不会出现1.0,也就是永远不会停止计算。所以,现代计算机只能以近似值来存储0.1,这也是为什么C/C++代码中,0.1累加100次的结果却不是10。具体可参考 矢泽久雄《程序是怎么跑起来的》P43页。

2. 内存中存储方式

        IEEE规定的几种浮点型存储方式如下所示,以科学计数法的形式存储,符号位固定1bit,基数固定为2,尾数部分和指数部分的bit数根据实际精度加以区分。

       尾数部分用的是“将小数点前面的值固定为1的正则表达式”,比如对于0.625 (二进制0.101),则表示为1.01 * 2^(-1)。因为小数点前面的值固定为1,所以在实际存储时,小数点前面的1也被省略了。

       指数部分的值有正有负,规定使用EXCESS系统表现,单精度指数部分表示方法如图所示,参考 矢泽久雄《程序是怎么跑起来的》P52页。

       FP16和BF16都占16bit,但BF16有更大的优势:与FP32相比,BF16的指数部分bit数一样,只是尾数部分减少了16bit,可以很方便的从FP32转换为BF16格式(牺牲了一部分的精度)。而要想从FP32转换为FP16,就需要对指数部分进行移位操作,比较麻烦。