为什么在C语言中浮点数转换为int时会被舍入?

gdrx4gfi  于 2023-10-16  发布在  其他
关注(0)|答案(6)|浏览(169)

在一次采访中,有人问我对下面的代码有什么看法:

  1. #include <stdio.h>
  2. int main()
  3. {
  4. float f = 10.7;
  5. int a;
  6. a = f;
  7. printf ("%d\n", a);
  8. }

我回答说:

  • 编译器将发出警告,因为您正在将float更改为int而没有强制转换。
  • int将具有垃圾值,因为您没有使用强制转换。

然后,他们允许我在在线编译器上运行程序。我当时不知所措。我的两个假设都错了。编译器没有发出任何警告,并且int的值为10。即使我将float的值更改为10.9或10.3,答案也是一样的。即使放入cast也不会改变结果。
有人能告诉我为什么会发生这种情况,在什么情况下会有不同的结果。

**注意:**在编译时,面试官告诉我不要添加gcc标志。
**编辑:**现在我明白了,浮点数不会四舍五入,答案将是10。但是有人能解释一下为什么这个设计是这样的吗?为什么任何float在转换为int时都要四舍五入?有什么特别的原因吗?

y53ybaqx

y53ybaqx1#

这就是标准的6.3.1.4所说的:
当一个真实的浮点类型的有限值被转换为一个整数类型而不是_Bool时,小数部分被丢弃(即,该值被向零截断)。如果整数部分的值不能用整数类型表示,则行为未定义。
未定义的行为部分是关于整数的大小和符号。如果你选择了一个太小而不能包含结果的整数,它会调用undefined行为。
int将有垃圾值,因为您没有使用强制转换
在溢出的情况下,int * 可能有一个垃圾值,但是否存在强制转换与此无关。
编译器从来不需要显示“警告”,警告不是C标准指定的东西,C标准只提到 * 诊断 *(即某种消息,称之为错误或警告)。因此,您永远不能可移植地假设每个编译器都会给出任何类型的给予警告。
在这种隐式浮点数到整型数转换的情况下,编译器根本不需要显示任何形式的诊断。好的编译器会。
在GCC的情况下,它对这样的警告相当草率,你必须明确地告诉它通过添加-Wconversion-Wfloat-conversion来给予警告。这些是暗示的额外标志。

9gm1akwq

9gm1akwq2#

许多转换在C中是隐式的:

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

由于这些行为是由标准定义的,因此转换器通常不会发出有关这些行为的诊断,但其中一些转换指示编程错误,例如double x = 1 / 2;。为了帮助程序员避免这些愚蠢的错误,可以指示编译器发出警告甚至错误。
使用这些额外的警告是明智的。如果使用-Wall -Wextra -Wconversion -Wfloat-conversion调用,gcc将执行此操作,clang具有类似的设置:clang -Wall -Weverything:您的第一个假设并非不切实际,但gcc在默认情况下对草率的代码非常宽容,您被指示不要使用任何标志。更糟的是:为了与旧的Makefiles保持兼容,gcc默认为c89,并且会抱怨使用了一些c99特性。
请注意,当语言可以确定它们是必要的时,它会定义行为,但有时它不能:

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

在这种情况下,标准指定f应该作为double传递给可变参数函数printf,但是printf需要一个int参数作为说明符%d。你的第二个假设在这里是正确的,需要一个(int)转换的显式转换。同样,好的编译器可以根据指示对这些错误进行诊断。
此外,一些隐式转换可以在编译时确定为丢失甚至调用未定义行为的信息,例如:

  1. char x = 300;
  2. int x = 1e99;

如果编译器对这些问题发出诊断,即使没有严格的选项,也会有帮助。
最后,有些转换会丢失信息,但在一般情况下更难检测:

  1. double f = 10000000000000;
  2. char a = f;
  3. float f = d;
  4. int i = d;

只有当接收类型对于整数部分足够大时,语言才定义行为,否则即使使用显式强制转换,行为也是未定义的。在这些情况下,程序员是否需要警告是个人选择的问题。
关于为什么从浮点类型到整数类型的转换被定义为向0舍入,这样做可能更简单,如果您知道值为正数,则可以在转换之前使用round()或添加0.5来获得其他行为。

展开查看全部
mctunoxg

mctunoxg3#

根据编译器设置,将出现警告。
但根据C语言标准,该行为是绝对定义良好的,浮点值将向零舍入。a保证为10。因为你的回答错误地表明世界上几乎所有的C程序都是致命的,所以我不会雇佣你来做C的工作。

n3schb8v

n3schb8v4#

C几乎在所有东西之间都定义了一个转换,所以,几乎没有任何赋值会产生警告或错误,除非你启用了规范不要求的额外检查。
至于 * 为什么 * 它向零舍入,好吧,如果它在截断之前加(或减)0.5以便舍入,那么如果你想要的是截断,你就必须用开放代码撤销它,所以你不会比为了舍入而加0.5更糟糕。
此外,C是一种实用的语言,它的正常行为与其原始时代的实际机器上的可用指令相对应是很重要的。

4bbkushb

4bbkushb5#

当编译器隐式地将浮点数转换为int时,它不会向下舍入,甚至不会舍入。数字的小数部分被“截断”,即。小数点后的任何内容都被简单地删除。

ntjbwcob

ntjbwcob6#

我觉得你的推理很有道理。然而,C通常并不完全有意义;浮点值被隐式地向零舍入。你可以让编译器在类型被这样隐式转换时发出警告。

  1. /*test.c*/
  2. #include <stdio.h>
  3. int main()
  4. {
  5. double f;
  6. int a;
  7. f = 10.7;
  8. a = f;
  9. printf("%d\n", a);
  10. return 0;
  11. }

对于GCC,我们正在寻找的选项是-Wconversion

  1. $ c89 -pedantic -Wall -Wconversion test.c
  2. test.c: In function main’:
  3. test.c:9:6: warning: conversion to int from double may alter its value [-Wfloat-conversion]
  4. a = f;
  5. ^
展开查看全部

相关问题