C语言中的隐式类型转换

7
我在维基百科上偶然发现了以下示例(http://en.wikipedia.org/wiki/Type_conversion#Implicit_type_conversion)。
#include <stdio.h>

int main()
{
    int i_value   = 16777217;
    float f_value = 16777217.0;
    printf("The integer is: %i\n", i_value); // 16777217
    printf("The float is:   %f\n", f_value); // 16777216.000000
    printf("Their equality: %i\n", i_value == f_value); // result is 0
}

他们的解释是:“当i_value与f_value进行比较时,会发生隐式转换将i_value转换为float类型,这种转换会丢失精度,导致被比较的值不同。”
这不对吗?如果i_value被转换为float,那么两者都会有相同的精度损失,它们应该是相等的。 所以i_value必须转换为double。

使用g++(GCC 4.6.2),我得到了1表示相等。 - Kerrek SB
@Kerrek: 还有我。在VS中,我得到了0。 - Oliver Charlesworth
@OliCharlesworth:我对将字面值更改为f或类型更改为double很感兴趣——在所有情况下,我都得到了1... - Kerrek SB
你可能正在使用 x64 系统,它不使用 10 字节扩展,而是通常使用双精度。你也可以在 32 位 CPU 上通过 -mfpmath=sse -msse 观察到这一点。 - konrad.kruczynski
3个回答

7
不,对于等号运算符而言,会发生“通常算术转换”,其步骤如下:
  • 首先,如果任一操作数的相应实数类型为long double,则将另一个操作数转换为没有类型域更改的类型,其相应的实数类型为long double。
  • 否则,如果任一操作数的相应实数类型为double,则将另一个操作数转换为没有类型域更改的类型,其相应的实数类型为double。
  • 否则,如果任一操作数的相应实数类型为float,则将另一个操作数转换为没有类型域更改的类型,其相应的实数类型为float。
这里适用最后一种情况:i_value被转换为float。
虽然如此,你可以看到比较结果的奇怪之处是由于通常算术转换中这个警告所致:

浮点操作数和浮点表达式的结果可能以比类型要求更高的精度和范围表示;但类型不会因此而改变。

这就是正在发生的事情:转换后的i_value仍然是float类型,但在该表达式中,编译器利用此限度并以比float更高的精度表示它。当编译通往387兼容浮点数时,这是典型的编译器行为,因为编译器将临时值留在浮点堆栈上,该堆栈以80位扩展精度格式存储浮点数。
如果使用gcc编译器,您可以通过使用-ffloat-store命令行选项来禁用此附加精度。

在x64上,gcc使用显式的cvtsi2ssl指令将整数转换为浮点数。然而,在x86上,实际上会发生更高精度的情况,甚至比double还要高。 - konrad.kruczynski
@konrad.kruczynski:是的,你可以通过提供“-mfpmath=sse”选项(该选项还需要“-msse”或暗示它的选项)在x86上获得相同的结果。 - caf

0
这里有一些很好的答案。在各种整数和各种浮点表示之间进行转换时,必须非常小心。
我通常不会测试浮点数的相等性,特别是如果其中一个来自隐式或显式从整数类型转换而来。我处理的应用程序充满了几何计算。尽可能地,我们使用归一化整数(通过强制接受输入数据中我们将接受的最大精度)。对于必须使用浮点的情况,如果需要比较,我们将对差异应用绝对值。

-1

我相信32位IEEE浮点数可以容纳的最大整数值是1048576,比上面的数字要小。因此,浮点数值肯定无法精确地保持16777217。

我不确定的部分是编译器如何比较两种不同类型的数字(即浮点数和整数)。我可以想到三种不同的方法:

1)将两个值转换为“float”(这应该使值相同,因此编译器可能不会这样做)

2)将两个值转换为“int”(这可能会显示它们相同或不同...转换为int通常会截断,因此如果浮点值为16777216.99999,则转换为“int”将截断)

3)将两个值都转换为“double”。我猜测这就是编译器所做的。如果这是编译器所做的,那么这两个值肯定是不同的。双精度可以精确地保持16777217,它还可以精确地表示16777217.0转换为的浮点值(这不完全等于16777217.0)。


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