在x86/x64体系里,由于x87 FPU硬件使用扩展双精度格式,因此必然会遇到single/double precision格式与double extended-precision格式之间的互换问题。
当由单精度数或双精度转换为扩展双精度数时,exponent部分必须基于扩展双精度数的biased码来调整。于是扩展双精度数的exponent值为:
① 从单精度转化:exponent – 127 + 16383。
② 从双精度转化:exponent – 1023 + 16383。
而扩展双精度数的significand部分,由单/双精度数的significand部分移植过来。
以单精度数1.11...×2120为例,它转换为扩展双精度的过程如下所示。
单精度数1.11...×2120的编码值为0x7BFFFFFF,它的exponent值为0xF7(11110111B),significand部分全为1值。
于是扩展双精度数的exponent值为0xF7-127 + 16383=0x4077(1000 0000 0111 0111B),单精度23位的significand部分直接移到扩展双精度的bit62到bit40位,低40位补0。
最终的扩展双精度编码值为0x4077_FFFFFF00_00000000。而对于双精度数来说:52位的significand部分将直接移到扩展双精度的bit62到bit11位。
而从扩展双精度转换为单/双精度数的情形会复杂得多,涉及目标格式的precison(精度)问题。当扩展双精度significand部分的值超出目标格式的精度时,就会发生rounded(舍入)操作,从而引发precision异常。
要检查超出精度的significand是否为0值,如下所示。
目标格式 |
超出精度部分 |
备注 |
单精度数 |
bit 39~bit 0 |
是否为0值 |
双精度数 |
bit 10~bit 0 |
这部分不为0值时,就会发生rounded操作。
下面,我们以扩展双精度数1.11...×2120转化为单精度格式为例进行描述。当1.11...×2120为扩展双精度格式时,它的编码值为0x4077_FFFFFFFF_FFFFFFFF。
目标格式exponent部分的计算如下。
① 单精度数:exponent-16383+127。
② 双精度数:exponent-16383+1023。
这个转换过程较为复杂,如下所示。
图中的阴影部分是超出精度的significand部分(bit 39~bit 0),它的值不为0,需要进行rounded操作,在x87 FPU中这个舍入依赖于rounded控制位。
IEEE754定义了以下4种舍入模式。
① round to nearest模式:朝±∞(正和负方向的无穷大值)方向舍入。
② round down模式:正数朝最大normal值舍入,负数朝-∞方向舍入。
③ round up模式:正数朝+∞方向舍入,负数朝最大normal值舍入。
④ round zero模式:正数和负数都朝最大normal值舍入。
上图中的舍入是朝+∞方向舍入,如图所示:bit 39的值为1,它将向bit 40进行舍入,效果等于+1值。目标格式中的significand部分舍入的结果值为0。
目标格式的exponent部分为扩展双精度的exponent-16383+127=0xF7(11110111B),可是由于significand部分还是进位值,因此目标格式的最终exponent部分为0xF8(加上1值)。
因此,最终转换的单精度值为0x7C000000,转换得到的浮点数是1.0...×2121,结果大于原来的扩展双精度浮点数。
这和转换为单精度数是一致的。在双精度格式里,它的精度是52位,因此超出精度部分为bit10到bit0位。
exponent的计算是扩展双精度的exponent-16383+1023。
本文节选自《x86x64体系探索及编程》
电子工业出版社出版
邓志著