13

所以我搜索了这个主题,并没有发现任何与它真正相关的内容。

我试图查看这个简单代码背后的程序集:

int main(int argc, char *argv[])
{
    double d = 1.0;
    float f = static_cast<float>(d);

    system("PAUSE");
    return 0;
}

这是(使用 Visual Studio 2012):

    15:     double d = 1.0;
000000013FD7C16D  movsd       xmm0,mmword ptr [__real@3ff0000000000000 (013FD91AB0h)]  
000000013FD7C175  movsd       mmword ptr [d],xmm0  
    16:     float f = static_cast<float>(d);
000000013FD7C17B  cvtsd2ss    xmm0,mmword ptr [d]  
000000013FD7C181  movss       dword ptr [f],xmm0

我对组装不太满意,但无论如何我都试图分析它。所以前两行似乎是将双精度值移动3ff0000000000000到寄存器中,然后将寄存器的内容移动到d的内存地址。

然后,我只是不知道下一行是什么。该cvtsd2ss操作显然是将双精度浮点值转换为单精度浮点值的指令,但我找不到该指令的实际作用。(然后将转换后的值移动到 f 的内存空间)。

所以我的问题是,这个转换实际上是如何通过这条指令完成的?我知道 C++ 强制转换会在另一种类型中产生最接近的值,但除此之外,我不知道实际执行的操作......

4

1 回答 1

15

cvtsd2ss指令使用 FPU 的舍入模式进行转换。默认舍入模式是舍入到最近的偶数。

为了遵循该算法,请记住IEEE 754-1985 Wikipedia 页面上的信息,尤其是表示布局的图表。

首先,计算目标的指数floatdouble类型具有比 更宽的范围float,因此对于非常小的 ,结果可能是0.0f(或非正规)double,或者对于非常大的双精度数可能是无限值。

对于将法线double转换为法线的通常情况float(粗略地说,当 的无偏指数double可以用单精度表示的 8 位表示时),目标有效位的前 23 位开始与原始数字的 52 位有效数的最高有效位。

然后就是四舍五入的问题:

  • 如果剩余位低于10..0,则目标有效位保持原样。

  • 如果剩余位高于10..0,则增加目标有效位。如果增加它会使其溢出(因为它已经是1..1),那么进位将传播到指数位。由于精心设计了 IEEE 754 布局,这会产生正确的结果。

  • 如果剩下的位正好是10..0,那么正好是两个s 的double中间。float在这两个选项中,选择最后一位0(“偶数”)的那个。

在这一步之后,目标有效数字对应于float最接近原始有效数字double

定向舍入模式只是更简单。目标float是非正规的情况稍微复杂一些(必须小心避免“双舍入”)。

于 2013-05-24T14:45:26.350 回答