将int转换为在C中浮动时的奇怪行为

fav*_*avq 22 c floating-point casting visual-studio-6 gcc3

我对以下C程序的输出有疑问.我尝试使用Visual C++ 6.0和MinGW32(gcc 3.4.2)编译它.

#include <stdio.h>

int main() {
    int x = 2147483647;
    printf("%f\n", (float)2147483647);
    printf("%f\n", (float)x);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出是:

2147483648.000000
2147483647.000000
Run Code Online (Sandbox Code Playgroud)

我的问题是:为什么两条线都不同?将整数值2147483647转换为IEEE 754浮点格式时,它将近似为2147483648.0.所以,我预计这两行都将等于2147483648.000000.

编辑:值"2147483647.000000"不能是单精度浮点值,因为数字2147483647不能精确地以IEEE 754单精度浮点格式表示而不会损失精度.

chu*_*ica 11

在这两种情况下,代码都试图从某种整数类型转换为float然后转换为double.. double转换发生,因为它是float传递给可变参数函数的值.

检查您的设置FLT_EVAL_METHOD,怀疑它的值为1或2(OP报告2至少有一个编译器).这允许编译器评估float"...范围和精度的操作和常量"大于float.

您的编译器优化(float)x直接int进行double算术运算.这是运行期间的性能改进.

(float)2147483647是一个编译时演员和优化编译器int,以floatdouble精度性能没有这里的问题.


[编辑2]有趣的是,C11规范比C99规范更具体,增加了"除了赋值和转换......".这意味着C99编译器有时允许intdouble直接转换,无需先经历float和C11进行了修订,明确不允许跳过演员.

由于C11正式排除了这种行为,现代的编制者不应该这样做,但是较旧的,比如OP可能 - 因此是C11标准的错误.除非发现某些其他C99或C89规范另有说明,否则这似乎是允许的编译器行为.


[编辑]由@Keith Thompson,@ tmyklebu,@ Matt McNabb一起评论,编译器,即使是非零FLT_EVAL_METHOD,也应该产生2147483648.0....因此,编译器优化标志明确地覆盖了正确的行为,或者编译器有角落错误.


C99dr§5.2.4.2.28具有浮动操作数的操作值和通常算术转换以及浮动常量的值将被评估为其范围和精度可能大于该类型所需的格式.评估格式的使用以FLT_EVAL_METHOD的实现定义值为特征:

-1不确定;

0仅根据类型的范围和精度评估所有操作和常量;

1评估操作和类型的常量floatdouble向范围和精度double类型,评估long double操作和常数的范围和精度long doubletype`;

2评估所有long double类型的范围和精度的操作和常量.


C11dr§5.2.4.2.29除了赋值和强制转换(删除所有额外的范围和精度)之外,具有浮动操作数的运算符产生的值和通常算术转换以及浮动常量的值被评估为其范围的格式和精度可能大于类型所要求的精度.评估格式的使用由FLT_EVAL_METHOD的实现定义值表征

-1(与C99相同)

0(与C99相同)

1(与C99相同)

2(与C99相同)

  • 这个答案是对的.无论`FLT_EVAL_METHOD`的值是什么,对`float`的显式转换都会抛弃过多的精度.这是C标准所要求的.如果MSVC没有这样做,那就是编译器中的一个错误. (3认同)
  • 该段以"除了赋值和转换"开头,但我们讨论的代码是*cast*,因此本段的其余部分不适用(特别是FLT_EVAL_METHOD的相关性) (2认同)

M.M*_*M.M 7

这肯定是一个编译器错误.从C11标准我们有以下保证(C99类似):

  • 类型具有一组可表示的值(隐含)
  • 所有可表示的值float也可由double (6.2.5/10)表示
  • 转换floatdouble不更改值(6.3.1.5/1)
  • 铸造intfloat,当int值是设定为表示的值float,使该值.
  • 铸造intfloat,当int值的量值小于FLT_MAXint不是用于表示值float,使得要么下一个最高或次最低float要被选择的值,并且该一个被选择为实现定义的.(6.3.1.4/2)

这些点中的第三个保证float提供的值printf不会被默认参数提升修改.

如果2147483647是在表示的float,然后(float)x(float)2147483647必须给2147483647.000000.

如果2147483647是没有代表性的float,然后(float)x(float)2147483647必须要么给下一个最高或次最低float.他们不必同时做出相同的选择.但这意味着2147483647.000000不允许打印输出1,每个打印输出必须是更高的值或更低的值.


1嗯 - 理论上可能的是2147483646.9999999...,当值以6位精度显示时,下一个最低的浮点数是这样的,printf然后它被舍入以给出所看到的内容.但是在IEEE754中并非如此,您可以轻松地尝试折扣这种可能性.