转换为int时浮点数为何在C下面舍入?

在一次采访中,有人问我对以下代码有何看法:

#include  int main() { float f = 10.7; int a; a = f; printf ("%d\n", a); } 

我回答了:

  • 当您将float更改为没有float转换的int ,编译器将发出警告。

  • 由于您没有使用强制转换,因此int将具有垃圾值。

然后,他们允许我在在线编译器上运行程序。 我不堪重负。 我的假设都是错误的。 编译器没有发出任何警告,并且int的值为10.即使我改变了, float值为10.9或10.3,答案也是一样的。 即使投了一个cast并没有改变结果。

有人能告诉我为什么会发生这种情况,结果会在什么情况下有所不同。

注意:在编译时,面试官告诉我不添加gcc标志。


编辑:现在我已经明白,浮动不会四舍五入,答案将是10.但有人可以解释我为什么这样设计? 转换为int时为什么浮动,在下面四舍五入? 有具体原因吗?

这就是标准6.3.1.4所说的内容:

当实数浮动类型的有限值被转换为除_Bool之外的整数类型时,小数部分被丢弃(即,该值被截断为零)。 如果整数部分的值不能用整数类型表示,则行为是未定义的。

未定义的行为部分是关于整数的大小和符号。 如果选择的整数太小而无法包含结果,则会调用未定义的行为。

由于您没有使用强制转换,因此int将具有垃圾值

如果溢出,int 可能具有垃圾值,但是存在或不存在与其无关。

编译器永远不需要显示“警告”,警告不是C标准指定的东西,它只提到诊断 (即某种消息,称之为错误或警告)。 因此,您永远不能假装每个编译器都会发出任何类型的警告。

在这种隐式float-to-int转换的情况下,编译器根本不需要显示任何forms的诊断。 但是,好的编译器会。

在GCC的情况下,这种警告相当草率,您必须明确告诉它通过添加-Wconversion-Wfloat-conversion来发出警告。 这些是暗示的额外标志。

许多转换隐含在C中:

  • 在所有数字类型之间,
  • 在数据指针类型和void *类型之间,
  • 从数组类型到其对应的指针类型(这称为衰减)
  • 从非const类型到其const等价物,

编译器通常不会对这些进行诊断,因为行为是由标准定义的,但其中一些转换表明编程错误,例如double x = 1 / 2; 。 为了帮助程序员避免这些愚蠢的错误,可以指示编译器发出警告甚至错误。

使用这些额外的警告是明智的。 如果使用-Wall -Wextra -Wconversion -Wfloat-conversion调用gcc将会执行此操作,并且clang具有类似的设置: clang -Wall -Weverything 。 你的第一个假设并不是不切实际的,但是默认情况下gcc对于草率代码是非常宽容的,并且你被指示不使用任何标志。 更糟糕的是:要保持与旧的Makefile兼容, gcc默认为c89,并会抱怨使用一些c99function。

请注意,语言定义了可以确定它们是必要的行为,但有时它不能:

 float f = 10.7; printf("%d\n", f); 

在这种情况下,标准指定f应作为double传递给variadic函数printf ,但printf期望指定符%dint参数。 你的第二个假设在这里是正确的,需要使用(int)强制转换进行显式转换。 同样,如果指示这样做,优秀的编译器可以对这些错误发出诊断。

此外,可以在编译时确定一些隐式转换,以丢失甚至调用未定义行为的信息,例如:

 char x = 300; int x = 1e99; 

如果编译器甚至在没有严格选项的情况下为它们发出诊断信息也会有所帮

最后,一些转换会丢失信息,但在一般情况下更难以检测:

 double f = 10000000000000; char a = f; float f = d; int i = d; 

仅当接收类型对于整数部分足够大时,语言才会定义行为,否则即使使用显式强制转换,行为也是未定义的。 程序员是否想要在这些情况下发出警告是个人选择的问题。

关于为什么从浮点类型转换为整数类型被定义为向0舍入的原因,这样做可能更简单,如果您知道值,则可以通过使用round()或在转换前添加0.5来获得其他行为是积极的。

将根据编译器设置发出警告。

但根据C语言标准,行为绝对定义良好,浮点值将向零舍入。 a保证是10.因为你的答案错误地表明世界上每个C程序都被致命地打破了,所以我不会雇用你从事C工作。

C在几乎所有内容之间都定义了转换,因此,除非您启用规范不需要的额外检查,否则几乎没有任何赋值会产生警告或错误。

至于为什么它向零舍入,好吧,如果它在截断之前添加(或减去)0.5以便舍入,那么你必须用开放代码撤消它,如果你想要的是截断,那么你不是更糟关闭,如果你必须加0.5才能圆。

此外,C是一种实用语言,重要的是它的正常行为与其原始时代的实际机器上的可用指令相对应。

我认为你的推理很有道理。 然而,C通常没有完美的意义; 浮点值隐式舍入为零。 当类型被隐式转换时,您可以使编译器发出警告。

 /*test.c*/ #include  int main() { double f; int a; f = 10.7; a = f; printf("%d\n", a); return 0; } 

使用GCC我们正在寻找的选项是-Wconversion

 $ c89 -pedantic -Wall -Wconversion test.c test.c: In function 'main': test.c:9:6: warning: conversion to 'int' from 'double' may alter its value [-Wfloat-conversion] a = f; ^