程序的行为取决于float 和double 类型的实现:
(float)a/b 可能使用float 算术计算并产生与0.6 不同的结果,double 的类型。两者都可能是值 6 个 10 的近似值,使用以 2 为底的浮点系统无法准确表示。在您的系统上,前者大于后者,因此会打印 congratulations,但在不同的系统上行为可能会有所不同,尤其是在使用相同表示形式实现类型 float 和 double 的情况下。
这里有一个更详细的说明:
#include <stdio.h>
int main() {
int a, b;
printf("Enter a and b: ");
if (scanf("%d%d", &a, &b) != 2)
return 1;
printf("\na=%d, b=%d\n", a, b);
if ((float)a/b > 0.6) printf("(float)a/b > 0.6\n");
if ((float)a/b == 0.6) printf("(float)a/b == 0.6\n");
if ((float)a/b < 0.6) printf("(float)a/b < 0.6\n");
if ((float)a/b > 0.6F) printf("(float)a/b > 0.6F\n");
if ((float)a/b == 0.6F) printf("(float)a/b == 0.6F\n");
if ((float)a/b < 0.6F) printf("(float)a/b < 0.6F\n");
if ((double)a/b > 0.6) printf("(double)a/b > 0.6\n");
if ((double)a/b == 0.6) printf("(double)a/b == 0.6\n");
if ((double)a/b < 0.6) printf("(double)a/b < 0.6\n");
// printing the values (converted to double when passed to printf)
printf(" (float)a/b -> %.18g (%#a)\n", (float)a/b, (float)a/b);
printf("(double)a/b -> %.18g (%#a)\n", (double)a/b, (double)a/b);
printf(" 0.6F -> %.18g (%#a)\n", 0.6F, 0.6F);
printf(" 0.6 -> %.18g (%#a)\n", 0.6, 0.6);
return 0;
}
输出:
Enter a and b: 6 10
a=6, b=10
(float)a/b > 0.6
(float)a/b == 0.6F
(double)a/b == 0.6
(float)a/b -> 0.60000002384185791 (0x1.333334p-1)
(double)a/b -> 0.599999999999999978 (0x1.3333333333333p-1)
0.6F -> 0.60000002384185791 (0x1.333334p-1)
0.6 -> 0.599999999999999978 (0x1.3333333333333p-1)
如您所见,(float)a/b 与0.6F 的值完全相同,float 类型的常量,(double)a/b 与0.6 相同。 C 标准不保证这一点,但除法的结果和常量都会产生最接近目标类型精确值的近似值。如您所见,0.6F 实际上大于六十,0.6 更小。
还要注意编译器使用-Weverything 产生的许多有用警告:
clang -O3 -funsigned-char -Weverything -Wno-padded -Wno-shorten-64-to-32 -Wno-missing-prototypes -Wno-vla -Wno-missing-noreturn -Wno-sign-co
nversion -Wno-unused-parameter -Wwrite-strings -g -lm -lcurses -o sixtens sixtens.c
sixtens.c:12:17: warning: implicit conversion increases floating-point precision: 'float' to 'double' [-Wdouble-promotion]
if ((float)a/b > 0.6) printf("(float)a/b > 0.6\n");
~~~~~~~~^~ ~
sixtens.c:13:20: warning: comparing floating point with == or != is unsafe [-Wfloat-equal]
if ((float)a/b == 0.6) printf("(float)a/b == 0.6\n");
~~~~~~~~~~ ^ ~~~
sixtens.c:13:17: warning: implicit conversion increases floating-point precision: 'float' to 'double' [-Wdouble-promotion]
if ((float)a/b == 0.6) printf("(float)a/b == 0.6\n");
~~~~~~~~^~ ~~
sixtens.c:14:17: warning: implicit conversion increases floating-point precision: 'float' to 'double' [-Wdouble-promotion]
if ((float)a/b < 0.6) printf("(float)a/b < 0.6\n");
~~~~~~~~^~ ~
sixtens.c:16:20: warning: comparing floating point with == or != is unsafe [-Wfloat-equal]
if ((float)a/b == 0.6F) printf("(float)a/b == 0.6F\n");
~~~~~~~~~~ ^ ~~~~
sixtens.c:19:21: warning: comparing floating point with == or != is unsafe [-Wfloat-equal]
if ((double)a/b == 0.6) printf("(double)a/b == 0.6\n");
~~~~~~~~~~~ ^ ~~~
sixtens.c:22:53: warning: implicit conversion increases floating-point precision: 'float' to 'double' [-Wdouble-promotion]
printf(" (float)a/b -> %.18g (%#a)\n", (float)a/b, (float)a/b);
~~~~~~ ~~~~~~~~^~
sixtens.c:22:65: warning: implicit conversion increases floating-point precision: 'float' to 'double' [-Wdouble-promotion]
printf(" (float)a/b -> %.18g (%#a)\n", (float)a/b, (float)a/b);
~~~~~~ ~~~~~~~~^~
sixtens.c:24:45: warning: implicit conversion increases floating-point precision: 'float' to 'double' [-Wdouble-promotion]
printf(" 0.6F -> %.18g (%#a)\n", 0.6F, 0.6F);
~~~~~~ ^~~~
sixtens.c:24:51: warning: implicit conversion increases floating-point precision: 'float' to 'double' [-Wdouble-promotion]
printf(" 0.6F -> %.18g (%#a)\n", 0.6F, 0.6F);
~~~~~~ ^~~~
10 warnings generated.
正如 Brian Kernighan 和 P.J. Plauger 曾经说过的,*用浮点数进行算术运算就像移动一堆沙子。每次你这样做,你都会失去一点沙子并捡起一点泥土。
在 David Goldberg 1991 年的 ACM 论文What Every Computer Scientist Should Know About Floating-Point Arithmetic 中了解更多信息。