【问题标题】:C: Data Types & Casting with printf()C:使用 printf() 进行数据类型和强制转换
【发布时间】:2015-08-12 23:30:15
【问题描述】:

我对编程相当陌生,并且开始习惯 C。如果这是一个重复的问题,我深表歉意(我不知道底层过程,即要搜索什么)。

我正在使用一个简单的程序来适应数据类型的细微差别:

main(){
    int i;
    float a,b;


    i = 2;
    a = 2.0;
    b = 4.0;

    printf("%d %1.1f", i/b,a/b);
}

我希望程序打印 0 0.5(因为 a 和 b 都是浮点数,我将它们的比率打印为浮点数),但程序打印了 0 0.0(我正在使用 gcc -o)。但是,当我颠倒printf顺序(不切换对应变量的顺序)时,就是:

printf("%1.1f %d", i/b,a/b);

打印结果是 0.5 0。我不确定这里发生了什么。似乎在第一个程序中,b 在 i/b 中被转换为 int 并且未能在 a/b 中被转换为 float。但是,在第二个变体中, b 打印为两种不同的类型没有任何问题。整数不能被强制转换成浮点数吗?有人可以解释一下或指出正确的方向吗?

谢谢!

【问题讨论】:

  • 你能把gcc --version的结果贴出来吗?
  • 等等,这不是整数除法问题...
  • ab 都是浮点数,因此该除法的结果应该是 0.5,但将其打印为浮点数会导致 0.0...
  • @Schwern gcc (GCC) 4.6.3 20111208 (prerelease) using gcc -o, -Wall 给出了一些有用的警告。在第一个变体上:“%d”需要“int”类型的参数,但参数 2 的类型为 double。但是,我仍然在可执行文件中得到相同的 (0 0.0) 结果。

标签: c


【解决方案1】:

您正在调用未定义的行为。

printf 是一个可变参数函数,格式字符串与其他参数如何传递给函数之间没有联系。 / 的常用规则适用,因此 i/b 保持双精度并按原样传递给 printf,当函数尝试将其作为 int 读取时,这会导致未定义的行为。

如果你用 -Wall 编译你会看到这些警告

a.c: In function ‘main’:
a.c:12:12: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘double’ [-Wformat=]
     printf("%d %1.1f", i/b,a/b);
             ^

【讨论】:

  • 所以,修复方法是...要么对两个表达式使用%f 格式说明符,要么强制使用%d 说明符的表达式为int(也许通过使用一个演员来做到这一点)。
【解决方案2】:

我得到了不同的结果。

#include <stdio.h>

int main(){
    int i;
    float a,b;


    i = 2;
    a = 2.0;
    b = 4.0;

    printf("%d %1.1f", i/b,a/b);
}

1432933728 0.5

gcc -Wall 提供警告。您应该始终使用-Wall 运行以获得有用的警告。

$ gcc -Wall try.c 
try.c:12:24: warning: format specifies type 'int' but the argument has type 'float' [-Wformat]
    printf("%d %1.1f", i/b,a/b);
            ~~         ^~~
            %f
1 warning generated.

printf 在将浮点数解释为整数时做了一些奇怪的事情。 C 对数据不智能,只会将原始二进制浮点数作为整数应用。它通常会得到垃圾。

当我通过将其更改为 printf("%1.1f %1.1f", i/b,a/b); 来解决此问题时,我得到了 0.5 0.5 的预期结果。

这是苹果的“gcc”,其实是clang。

$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Target: x86_64-apple-darwin14.4.0
Thread model: posix

虽然我得到了不同的结果,但教训是您不能使用 %d 将浮点数转换为小数。通常,不会为您转换函数参数类型,即使是 printf。您必须使用显式类型转换来完成。你可以写这个...

printf("%1.1f %d", i/b, (int)(a/b));

注意额外的括号是必要的,因为类型转换的优先级高于除法。

您可以通过使用ceil(), floor(), round() and related functions 显式舍入浮点结果来获得更好的控制,而不是依赖类型转换。

【讨论】:

  • 并不奇怪,它只是将 IEEE 754 浮点格式视为二进制整数,这可以理解为一个很大的数字。
  • @Purag 仅当您已经了解 IEEE 浮点数时才有意义。大多数人认为数字就是数字。大多数程序员只有使用抽象内部表示的高级语言的经验。计算机表示数字和做数学的方式与我们在学校学习数学的方式几乎没有共同之处。是的,这很奇怪。你不认为这很奇怪……很奇怪。 :)
  • 对于刚刚学习的人来说确实很奇怪。但是,我将每个答案都视为剥离抽象层的机会,因为它只能帮助程序员准确了解幕后发生的事情。这就是我学习 C 的方式,我认为它对我有帮助。 :) 我不会低估阅读这个答案的人,所以我认为简要解释一下浮点是如何被解释的会更好。 :)
  • @Purag 我认为引入类型转换对于一个答案来说已经足够了。至于为什么 OP 从printf()0 0.0 而不是预期的0 0.5 得到那个奇怪的结果,我将其归结为printf() 中的某些内部状态在获得@987654335 的浮点数后被破坏@,或者优化器可能搞糊涂了。
  • @byrnesj1:%d 不会转换 i/b 到它的二进制表示。它已经在它的二进制表示中。 %d 格式导致printf 假设参数是int 类型。导致问题的原因是缺乏转换。 (转换会将一种类型的值转换为另一种类型的值,尽可能保持相同的数学值。)
猜你喜欢
  • 1970-01-01
  • 2014-03-07
  • 1970-01-01
  • 2012-06-20
  • 2011-01-04
  • 1970-01-01
  • 2021-01-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多