-4

In a C/C++ interview test I found some questions which I have not answered correctly, I used Visual C++ to check the results, I hope you can help me to understand them :

1)

int i=-3, j=2, k=0, m;
m = ++i && ++j || ++k; // k not incremented why ???
cout << i << " " << j << " " << k << " " << m; // -2 3 0 1 why it's not -2 3 1 1 !!!

=> why k is not incremented especially there is a ++ before it ? and I want to know the order in executing such line, I couldn't do this in debug mode.

Can you please give me a rule to follow to evaluate such expressions where there is stuff like ++variable or variable++ Thank you

2) why this comparison is false ?

float a = 5.2;
if(a == 5.2) // false
{}

when I add a float cast to 5.2 it works....

3)

int n()
{
    static int x = 0;
    return x++;
}

=> I thought that we always return 0 because I thought the compiler will translate "return x++" to : return x; x++; then we will never execute the incrementation...

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Aminos
  • 754
  • 1
  • 20
  • 40
  • 5
    There is no language C/C++. If that was asked in the interview, you should look for a different job. However, logical operators are explained in every C(++) book. – too honest for this site Sep 16 '15 at 21:19
  • 6
    1 is due to short-circuit evaluation. – Michelle Sep 16 '15 at 21:20
  • 3
    2 is due to a float and a double (5.2) not having the same representation, and also comparing real numbers for equality is likely to fail. Plenty of SO questions here about this. – Weather Vane Sep 16 '15 at 21:25
  • 1
    And 3 is because `static`. – juanchopanza Sep 16 '15 at 21:29
  • 1
    3: The incrementation operator doesn't have any idea about how you are going to use the resulting value. So it will, of course, execute the increment, as always. It is irrelevant, if you use it in a `return` or anywhere else. If the expression is evaluated, an increment happens. Period. And since `x` is static, the increment is permanent. – mastov Sep 16 '15 at 21:32
  • @juanchopanza Is it? It is simply because of the post increment operator used in the return statement. – syntagma Sep 16 '15 at 21:32
  • 3 is because `static int x = 0;` is only executed once, not on every function call. – Weather Vane Sep 16 '15 at 21:35
  • 1
    @REACHUS Maybe you misunderstood the "question". OP is surprised the return value isn't always 0. It is incremented by one each time the function it called. That is because `static`. Or one could say "because C++". – juanchopanza Sep 16 '15 at 21:35
  • @juanchopanza I think you misunderstood it. The OP is aware of the `static` part, but thought that the incrementation was "never executed" because it's inside a return statement. – mastov Sep 16 '15 at 21:37
  • @mastov I wouldn't assume that OP is aware of either of those things. – eerorika Sep 16 '15 at 21:43
  • 1
    1) because of lazy evaluation, you have `a && b || c`, `a` is True, `b` is True -> `a && b` is True, so the result will be True no matter what `c` value is, so it's not evaluated(executed); 2) by default if a real number is specified its type is _double_ (8bytes), you declared your var as _float_(4bytes) - loss of precision (in debug mode you'd see its value: 5.1999998); 3) because of `static` each time `n` will be called it will return a the value returned by its last call + 1 (first call returns 0). – CristiFati Sep 16 '15 at 21:45

3 Answers3

4

Question number 1:

The && operator has precedence over ||, so it will be evaluated first. Both terms (++i and ++j) have pre-increment, so first they are increased to -2 and 3 respectively, then they are ANDed. In C++, 0 is false and everything else is true. In this case, both terms are true (that is, non-zero), so the && returns true. Now it's time to test true || ++k. Here, an optimization kicks in: since true || anything will always be true, the compiler doesn't even test the expression. That is, it doesn't execute it. And k is not incremented. That's why it is a very bad habit to have the code inside if-statements do something - you can't be sure it will be run, depending on the condition. If you need it to run, make sure it is not put there or it could be optimized away.

Question number 2:

Floating point arithmetics is tricky - representing a number exactly is often not possible, and the numbers that are really used are just a very close approximation - close enough that it looks like it works, but if you go checking every small bit you notice that numbers aren't what they look like. By default, a number is treated as a double. A double and a float, though they look the same, are not the same. This has already been covered here: strange output in comparison of float with float literal. And I really recommend to read one of the linked articles, this one: "What Every Computer Scientist Should Know About Floating-Point Arithmetic"

Question number 3:

You are right that the returned value is 0 (the first time!), but then you think that instruction is split in two parts, the first of which being a return, which should cause the second one (the increment) to be skipped. No, it doesn't work like that. The increment is executed in any case. Post increment works like this: make a copy, increase the original, return the copy. Whatever called the post-increment sees the original value, but the increment does take place. And since x is static, the value will be kept, so that the next time the function n() is called, the initial value of x will be 1. And then 2, 3, and so on.

Community
  • 1
  • 1
3

Next time, when you have multiple questions, please ask them separately.

Answer to question 1:

Several things:

  1. Both the && and || operators force left-to-right evaluation1, and both introduce a sequence point; the left-hand operand will be evaluated and all side effects will be applied before the right-hand operand is evaluated;

  2. Both the && and || operators short circuit - if the value of the expression can be determined from the left-hand side operand, then the right-hand side operand will not be evaluated;

    • in the expression a || b, b will not be evaluated if a is non-zero;
    • in the expression a && b, b will not be evaluated if a is zero;
  3. && has higher precedence than ||, so a || b && c is parsed as a || (b && c);

  4. x++ evaluates to the current value of x, and as a side effect increments x;

  5. ++x evaluates to the current value of x plus 1, and as a side effect increments x.

So, the expression

++i && ++j || ++k

is parsed as

(++i && ++j) || ++k

and evaluated as follows:

  1. ++i is evaluated; the result is -2 which is not zero, so:
  2. ++j is evaluated; the result is 3, so:
  3. both -2 and 3 are non-zero, so:
  4. ++i && ++j evaluates to 1, so:
  5. ++k is not evaluated at all.

Answer to question 2:

Again, several issues:

  1. Most floating point values cannot be represented exactly; they are stored as approximations (you can't fit an infinite number of values into a finite number of bits);

  2. The floating-point constant expression 5.2 has type double, not float; to make it float, you would use a f suffix - 5.2f;

  3. float and double have different representations (double devotes more bits to both the exponent and fraction), so they will store different approximations for the same values, which is why the == comparison didn't work.

  4. Because of this, you should not use == to compare floating-point values; usually, you will take the difference between the two values and make sure that it's smaller than some epsilon value (bearing in mind that the epsilon value depends on magnitude).

Answer to question 3:

You are returning the result of the expression x++, but the expression still has the side effect of incrementing x (and the side effect is applied before the end of the return statement).


1. Most operators in C do not force a specific order of evaluation - given an expression like a + b * c, each of a, b, and c may be evaluated in any order. The result of b * c must be known before it can be added to the result of a, but that doesn't mean that either b or c must be evaluated before a.
John Bode
  • 119,563
  • 19
  • 122
  • 198
1
  1. C and C++ operate on short-circuit logic.

Consider the following:

bool myBool = (1 || 0+6*4);

myBool will evaluate as soon as it can. In this case, it will evaluate to true because the left argument in the || is true. It stops evaluating immediately. Same goes for &&. This is why it's idiomatic to add the more likely fail case on the left-side of boolean operators.

  1. It is false because floats cannot be represented perfectly by binary.

In this case, it would actually evaluate to true. However, consider the following example:

#include <iostream>

int main() {
   double i = 0.1;
   double total = 0.0;
   for(int j = 0; j < 10000; j++) {
      total+=i;
   }

   // total = 0.1*10000 = 1000... right?

   std::cout << std::boolalpha << "total == 1000.0 ? " << (total == 1000.0) << std::endl;

   return 0;
}

Here's the output of the above program. Comparing floats and doubles is a tricky thing in computing. Be careful doing it.

  1. Because it is static, it will be increment before return and remain its static value each time the function is called.

I won't get into this too much because it's outlined quite nicely in this question about lifetime of static variables. Essentially something that is static lives as long as the program regardless of visibility.

Community
  • 1
  • 1
erip
  • 16,374
  • 11
  • 66
  • 121