The behavior of your program depends on the implementation of the float
and double
types:
(float)a/b
is likely computed using float
arithmetics and produces a result different from 0.6
, which has type double
. Both may be approximations of the value six tens, which is not representable exactly using base 2 floating point systems. On your system, the former is greater than the latter so congratulations
is printed, but the behavior may differ on a different system, especially one where types float
and double
are implemented using the same representation.
Here is a more elaborate illustration:
#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;
}
Output:
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)
As you can see, (float)a/b
is exactly the same value as 0.6F
, the constant with type float
, and (double)a/b
the same as 0.6
. The C Standard does not guarantee that, but the result of the division and the constants both produce the closest approximation to the exact value for the target type. As you can see 0.6F
is actually larger than six tens and 0.6
smaller.
Note also the many useful warnings produced by the compiler with -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.
As Brian Kernighan and P.J. Plauger once said, *Doing arithmetics with floating-point numbers is like moving piles of sand. Each time you do it, you lose a little sand and pick up a little dirt.
Learn more about this in David Goldberg's 1991 ACM paper What Every Computer Scientist Should Know About Floating-Point Arithmetic.