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

时间:2016-01-26 07:14:57

标签: c casting floating-point int type-conversion

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

#include <stdio.h>

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

我回答:

  • 当您将float更改为int而没有演员表时,编译器会发出警告。

  • 由于您没有使用演员表,int会有垃圾值。

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

有人可以告诉我为什么会发生这种情况,结果会有什么不同。

注意:在编译时,采访者告诉我不要添加gcc标志。

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

5 个答案:

答案 0 :(得分:9)

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

  

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

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

  

由于您没有使用强制转换

,因此int将具有垃圾值

int 可能在溢出的情况下具有垃圾值,但是存在或没有强制转换与它无关。

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

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

对于海湾合作委员会而言,这种警告相当草率,你必须明确告诉它通过添加-Wconversion-Wfloat-conversion来发出警告。这些是暗示的额外标志。

答案 1 :(得分:2)

许多转换隐含在C:

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

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

使用这些额外的警告是明智的。 gcc如果使用-Wall -Wextra -Wconversion -Wfloat-conversionclang进行调用,则clang -Wall -Weverything会执行相同的设置:gcc。你的第一个假设并非不切实际,但默认情况下,gcc对于草率的代码是非常宽容的,并且你被指示不使用任何标志。更糟糕的是:要保持与旧的Makefile兼容,float f = 10.7; printf("%d\n", f); 默认为c89,并会抱怨使用某些c99功能。

请注意,该语言在确定必要时定义行为,但有时不能:

f

在这种情况下,标准指定printf应作为double传递给可变函数printf,但int需要%d参数对于说明符(int)。您的第二个假设在这里是正确的,需要使用char x = 300; int x = 1e99; 强制转换进行显式转换。同样,如果指示这样做,优秀的编译器可以对这些错误发出诊断。

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

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

如果编译器即使没有严格的选项就为这些发布了诊断信息,也会有所帮助。

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

0

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

关于为什么从浮点类型到整数类型的转换定义为向round()舍入的原因,这样做可能更简单,并且您可以通过使用0.5或添加来获取其他行为转换前<?php $dataRow = array( array( array( "CC-00057", 1, "apple", 45, "pc", "A1011", 1101, 42110, "asd" ) ), array( array( "CC-00057", 2, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 3, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 4, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 5, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 6, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 7, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 8, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 9, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 10, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 11, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 12, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 13, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 14, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 15, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 16, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), array( array( "CC-00057", 17, "grapes", 2, "box", 5, "A1012", 1101, 42111, "asd" ) ), ); echo '<h2>The $dataRow Array</h2><pre>'; var_dump ($dataRow); echo '</pre>'; if (isset($dataRow)) { echo '<h2>The Result</h2><pre>'; // Outer-most array foreach($dataRow as $row => $dataVal) { // Data arrays are in $dataVal[0], the inner-most array // -> Display the data array $res = ''; foreach($dataVal[0] as $key => $value) { $res .= ((empty($res)) ? '' : ', ') . "$key=$value"; } echo "Row $row: " . $res . PHP_EOL; } echo '</pre>'; } ?> ,如果您知道该值为正数。

答案 2 :(得分:0)

根据编译器设置会出现警告。

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

答案 3 :(得分:0)

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

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

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

答案 4 :(得分:-3)

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

/*test.c*/

#include <stdio.h>

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;
      ^