c++comma-operatorg++4.8

C++ : parentheses ignored when evaluating sequential operators


First of all, I must say that the problem raised here is solved for me, and I wonder :


I want to calculate the norm of a 2-dimentional vector, of which coordinates are computed only at that moment. Let's say, I want to calculate || (x0, y0) - (x1, y1) || , by the formula sqrt((x0-x1)**2 + (y0-y1)**2) . Only the result needs to be saved.

For performance reasons, the square is done by self-multiplication, and I want the substractions and access to variables to be done only once. I want the total to be efficient at runtime, and somehow elegantly coded. I have thought three possibilities :

I decided to try the last option.


Consider now the following code :

#include <cmath>
#include <stdio.h>

int main (void)
{
    float x0 (-1), x1 (2), y0 (13), y1 (9), result, tmp;

    result = std::sqrt ((tmp = x0 - x1, tmp * tmp) + (tmp = y0 - y1, tmp * tmp));
    printf ("%f\n", result);
}

I know I must obtain 5.000000, but I obtain 5.656854, which is sqrt((y0-y1)**2 + ((y0-y1)**2)).

I can get the wanted result with :

#include <cmath>
#include <stdio.h>

int main (void)
{
    float x0 (-1), x1 (2), y0 (13), y1 (9), result, tmp, tmp2;

    result = std::sqrt ((tmp = x0 - x1, tmp * tmp) + (tmp2 = y0 - y1, tmp2 * tmp2));
    printf ("%f\n", result);
}

It is like the first parts of the sequential operators are evaluated at first, ignoring the parentheses and the return value of the first sequential operator. Seems a bit akward ; is there something in the definition of C++ that I missed here ?

NB : Setting the optimization on or off changes nothing during the test.


Solution

  • Both sides of the operator + may be evaluated interleaved. In particular, each of the two assignments in the parenthesis must happen before the multiplication to its immediate right (in correct code that is, which your first variant is not), but need not happen before the other assignment. Also, the other assignment is allowed to happen in between one assignment and the matching multiplication.

    Your first variant thus invokes undefined behavior because it contains two unsequenced modification of tmp. So literally every result is legal, including a crash or NaN.

    In general, please keep in mind that "your code is clever" is not a compliment. Keep it simple, if you really must optimize away the subtractions (you most likely do not):

    auto dx = x0 - x1;
    auto dy = y0 - y1;
    auto result = std::sqrt(dx * dx + dy * dy);