C++中浮点数的转换是如何实现的?(从double到float或从float到double)

14

所以我搜索了这个主题,但没有找到真正相关的内容。

我尝试查看这个简单代码背后的汇编:

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++的强制类型转换会产生最接近另一种类型的值,但除此之外,我对实际执行的操作一无所知...


我知道C++的类型转换会产生最接近另一种类型的值。你还需要知道什么? - David Heffernan
英特尔软件开发手册第二卷。Intel Software Developer's Manual - Carl Norum
1
你可能想在Intel架构参考手册中阅读相关内容(警告:大型PDF),特别是从第241页开始。 - Jerry Coffin
你是在询问实际的算法吗? - harold
@DavidHeffernan 哦,我不是那个意思,我只是在想。 - JBL
显示剩余2条评论
1个回答

16

cvtsd2ss指令使用FPU的舍入模式进行转换,舍入模式默认为“四舍五入到最近偶数”。

为了遵循算法,需要牢记IEEE 754-1985 Wikipedia页面上的信息,特别是表示布局的图表。

首先计算目标float的指数: double类型具有比float更广泛的范围,因此对于非常小的double可能会得到0.0f(或非规格化),对于非常大的double可能会得到无限值。

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

然后是舍入问题:

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

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

  • 如果剩余的位恰好为10..0 ,则double恰好处于两个float之间。在这两个选择中,选择最后一位为0(“偶数”)的那个。

在此步骤之后,目标有效数位对应于原始 double 最近的 float
定向舍入模式更加简单。当目标 float 是一个非规格化数时会稍微复杂一些(必须小心避免“双重舍入”)。

好的,但在问题中的代码中,一个double被转换为一个single。 - David Heffernan
2
甚至单精度到双精度的转换也不能简单地移动位。正常值需要将指数加896。非正常值需要找到第一个位设置,移动有效数字并调整指数。 - Eric Postpischil
@EricPostpischil 所有原始的非规格化值在进行四舍五入的双精度->单精度转换时都会转换为 0.0f。当结果是一个非规格化数时,与正常->正常情况相比,剩余的位可以用诸如“10..0”之类的正则表达式来描述,无论如何,我不打算详细说明它。如果您认为这不能被省略,请随意编写您自己的答案。当本答案提到“指数”时,它是“无偏指数”(或“偏置指数”),对于浮点数1.0,两者都为零。 - Pascal Cuoq
@PascalCuoq 关于“所有原始非规格化值在舍入到最近的double->float转换中都会转换为0.0f”。这很有趣,即使是那些更接近最小可能浮点表示而不是零的非规格化值也是这样吗? - JBL
3
这个回答主要关注double到float的转换,因为这本身就很棘手。Double denormal(指的是非规格化数)位于-DBL_MIN和DBL_MIN之间(约为2*10^-308)。最小的严格正浮点数为~1.401298e-45,因此对于这种特定的转换,没有任何一种denormal输入数字会在四舍五入中舍入为非零数。 - Pascal Cuoq
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接