1

I found a strange (to me) behavior of casting to int in C. Appologies if that's a basic question but I'm unable to find the answer to why the following code produces an unexpected result.

#include <stdio.h>
int main(void)
{
    printf("1000 * 0.1 = %d\n", (1000 * 0.1));
    printf("1000 * (10/100) = %d\n", (1000 * (10/100)));
    printf("(int)1000 * 0.1 = %d\n", (int)(1000 * 0.1));
    printf("(int)1000 * (10/100) = %d\n", (int)(1000 * (10/100)));

    return 0;
}

Result with both -O0 and -O3 is the same:

1000 * 0.1 = -957043896
1000 * (10/100) = 0
(int)1000 * 0.1 = 100
(int)1000 * (10/100) = 0

I expect a non-sensical result for the first two (I don't know why but I expect passing a double to int argument shouldn't work). However the difference between 3 and 4 is puzzling to me. I expected (10/100) to be calculated on compile time and render the same result as 3.

Can someone explain to me why such result happens, and what is the proper/safe way to do integer-based divisions here?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
kiler129
  • 1,063
  • 2
  • 11
  • 21
  • 2
    In `printf("1000 * 0.1 = %d\n", (1000 * 0.1));` the `%d` promises you will pass `int` but you have actually passed `double`. A good compiler will issue a warning about it. – Weather Vane Sep 20 '21 at 18:56
  • First `printf` is undefined behavior, as it takes `double`s while the format specifier is for `int` – Eugene Sh. Sep 20 '21 at 18:56
  • 1
    `10/100` is integer math and will be `0`. – 1201ProgramAlarm Sep 20 '21 at 18:57
  • So does it assume that the whole expression, i.e. both parts, will be in integer math? – kiler129 Sep 20 '21 at 18:59
  • Assume is the wrong word. It **is** integer math, unless there's a cast or one of the operands is a float constant. – sj95126 Sep 20 '21 at 19:02
  • I would strongly suggest studying C's operator precedence rules. You could get rid of _a lot_ of parentheses from your code and easier code to read is generally easier to reason about. – Chris Sep 20 '21 at 19:03
  • @Chris: these were originally in `#define`s and I started just copying them together to find where the problem is - that's why they have way too many parenthesis. Obviously it's not the final code :) – kiler129 Sep 20 '21 at 19:06
  • I think part of the confusion here is because there's a difference between evaluating an expression, which in this code is done in the parentheses for the parameters to `printf()`, and type conversion, which perhaps OP _thinks_ is done when passing those parameters, but which actually isn't, and which is giving rise to undefined behavior. It'd be much simpler if these values were assigned to variables of known types, rather than being passed to `printf()` – Tim Randall Sep 20 '21 at 19:53

2 Answers2

5
printf("1000 * 0.1 = %d\n", (1000 * 0.1));
//                           int    double

int * double gives a double. You're trying to print a double with "%d" which is Undefined Behaviour (usually written UB). Try printf("1000 * 0.1 = %f\n", 1000 * 0.1);

printf("1000 * (10/100) = %d\n", (1000 * (10/100)));
//                                int    int int

int / int does integer division, with no decimals. 10/100 yields 0 (and a remainder of 10)

printf("(int)1000 * 0.1 = %d\n", (int)(1000 * 0.1));
//                                     int    double

the double 100.0 is converted to int and printed naturally. Note that it's possible that 1000 * 0.1 would generate 99.999999635264318 which would convert to 99

printf("(int)1000 * (10/100) = %d\n", (int)(1000 * (10/100)));
//                                          int    int int

10/100 is integer division ... is 0 in this case, same as 2nd statement above.

pmg
  • 106,608
  • 13
  • 126
  • 198
2

There is a difference for example between these two calls of printf

printf("1000 * 0.1 = %d\n", (1000 * 0.1));
printf("1000 * (10/100) = %d\n", (1000 * (10/100)));

In the firs call the expression 1000 * 0.1 has the type double but you are using the incorrect conversion specifier %d that is designed for objects of the type int.

In the second call the expression 1000 * (10/100) indeed has the type int. Pay attention to that the casting of the expression to the type int like (int)(1000 * (10/100)) is redundant and does not make a sense. Instead you could write for example ( int )( 1000 * (10.0/100) ). So this call is correct. However the value of the original outputted expression is equal to 0 due to the integer arithmetic.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • The biggest issue for me are the calls 3 and 4 - I expected the 4th call to return 100 as well. – kiler129 Sep 20 '21 at 19:00
  • @kiler129 There is no difference between the 2-nd and 4-th calls of printf. – Vlad from Moscow Sep 20 '21 at 19:02
  • @kiler129 `(int)(1000 * (10/100))` is `(int)(1000 * 0)` or `0`. – chux - Reinstate Monica Sep 20 '21 at 19:02
  • The `(int)` cast is irrelvant, since there is no floating point anywhere. `10/100` does not automatically produce a floating point value but `0`. – Weather Vane Sep 20 '21 at 19:04
  • So shouldn't `(int)(1000 * (float)(10/100))` render a correct result, as I'm specyfing that indeed that `10/100` is a float? Because even with explicit `(float)` cast for that part the result is still 0. I tried looking at compiler explorer but x86 ASM for these operations is beyond my understanding. – kiler129 Sep 20 '21 at 19:04
  • 3
    No, it doesn't. It divides 10 by 100 and *then* casts the quotient 0 to a float. – Weather Vane Sep 20 '21 at 19:05
  • @kiler129 See my updated post. – Vlad from Moscow Sep 20 '21 at 19:11
  • @VladfromMoscow: I see, now it makes sense - `10.0/100` is then forced to be treated as a float. Is there any digestible source where I can read about that behavior? Since even `(float)` didn't help as exactly as stated it will first divide as integers and then cast to float instead of dividing as floats. – kiler129 Sep 20 '21 at 19:12
  • 2
    @kiler129 Detail: "10.0/100 is then forced to be treated as a float." --> not quite. `10.0/100` is a `double`, not `float`. – chux - Reinstate Monica Sep 20 '21 at 19:16
  • 1
    @kiler129 Not sure what kind of "digestible source" I can refer you to, but the question of "why does 10/100 do integer division?" is a very old one, asked and answered many hundreds of times before. The answer is that, in C at least, when you have `x / y` — and in fact this is equally true for `x op y`, for *any* operator `op` — *the operation is **always** performed based on the types of its operands*. The compiler never "looks outward" to see what you're going to be doing with the result. If you write `(float)(i / j)`, by the time the compiler sees the cast, it's too late. – Steve Summit Sep 20 '21 at 19:36