朝负无穷舍入的快速32位float转32位int x87 FPU实现原理是什么?
x87 FPU 魔术浮点转整数实现原理
这个实现是早期x86平台上常用的高性能float转int优化技巧,核心是利用IEEE 754单精度浮点数的存储结构,避开开销极高的x87浮点转整数专用指令fist,整个逻辑可以拆解为以下步骤:
前置知识
- IEEE 754单精度float的存储结构:32位从高到低依次为1位符号位、8位阶码、23位尾数,真值公式为
(-1)^符号位 * (1 + 尾数/2^23) * 2^(阶码 - 127) - 当float的阶码为
149(即127+22)时,真值的小数点刚好落在23位尾数的末尾,整个浮点数的有效位刚好对应24位无小数的整数 - x87的
fstps指令存储浮点数时,会遵循当前FPU的舍入模式(你环境中设置的是朝负无穷舍入)对超出精度的小数位做舍入处理
逐行逻辑拆解
我们设待转换的float为x,最终要得到的朝负无穷舍入的整数为x_int,且要求x的取值范围在[-2^22, 2^22 - 1]内,超出范围会失效:
flds from:将待转换的x加载到x87栈顶fadds magic1:给x加上3*2^21(即6291456.0)。这个magic数的真值为1.1B * 2^22,对应的阶码刚好是149。加上范围在[-2^22, 2^22-1]的x后,结果的阶码仍然保持149,x的整数部分会被对齐到尾数的低位,小数部分会在后续存储时被舍入fstps to:将相加结果存为单精度float。这一步会按照朝负无穷的规则舍入掉所有小数位,此时内存中存储的4字节float的二进制编码,作为整数读取时满足以下关系:
这个推导的核心是:阶码149对应的固定编码偏移为编码的整数值 = 0x4AC00000 + 2 * x_int0x4A800000,加上magic1带来的固定尾数偏移0x400000,刚好得到固定常量0x4AC00000,而x_int会以2倍的形式存储在尾数段movl to,%eax:把float的二进制编码直接作为整数加载到eaxsubl $magic2,%eax:减去固定偏移0x4AC00000,此时eax的值为2*x_intsarl $1,%eax:算术右移1位,等价于带符号除以2,得到最终的x_int,算术右移可以保证负数也符合朝负无穷舍入的规则movl %eax,to:存储结果
额外说明
这个技巧在早年x86平台上非常流行,因为fist指令需要修改FPU控制字切换舍入模式,单次调用开销是这个实现的3~5倍。直到SSE指令集出现后,cvttss2si等专用浮点转整数指令普及,这个魔术实现才逐渐被淘汰。
内容的提问来源于stack exchange,提问作者user1636349
相关产品推荐
相关产品推荐

