5

This snippet works just fine.

for (int i = 0; i < 1; i++) // for some other purpose
{
  // some other code

  double** angle = new double* [10];   // for a 2D matrix
  for (int j = 0; j < 10; j++)
  {
     angle[j] = new double [3];

     if (j == 0) 
            angle[j][0] = 2;          // focused on the first column for now
     else 
            angle[j][0] = angle[j-1][0]+3;

     std::cout << angle[j][0] << std::endl;
  }

  for (int i = 0; i < 10; i++)
     delete[] angle[i];
     delete[] angle; 
}

I am trying to not use conditional statement inside the loop. If I replace that with the following line, the code stops working. Please help me understand it.

angle[j][0] = (j == 0) * 2 + (j != 0) * (angle[j-1][0] + 3);

Using g++ -std=c++11 -o out main.cpp; ./out on Ubuntu 16.04 LTS

CinCout
  • 9,486
  • 12
  • 49
  • 67
  • For what purpose you want to void using conditional statement? Readability? Performance? Also formatting of the loop with delete[] is misleading. – Konstantin Stupnik Dec 18 '19 at 06:59
  • @KonstantinStupnik: Thank you for pointing it out. I was listening to a professor's lecture and he kind of suggested it. Having a non-IT background, I do not have much time to verify or cross-check his statement. I will appreciate if you fix the deallocation. Please share your opinion. – user12556559 Dec 18 '19 at 07:06
  • 1
    Although a compiler may optimise, it is not strictly required to - math operations are not inherently short-circuited. So there is nothing to prevent the compiler from evaluating `angle[j-1][0] + 3` in your modified code when `j` is zero (in fact, it could be evaluated before the expressions `j==0` or `j != 0` are evaluated) - and that gives undefined behaviour. – Peter Dec 18 '19 at 07:42
  • At the very least unindent 'delete[] angle;' line, and I suggest to put curly braces around body of the loop with delete too. This is almost religious question whether to put braces around single statement body or not, but IMO in a long run it's better to always use them. – Konstantin Stupnik Dec 18 '19 at 09:42

3 Answers3

3

You're trying to use the ternary operator, but the syntax is wrong.

Do this:

angle[j][0] = (j == 0) ? 2 : (angle[j-1][0] + 3);

CinCout
  • 9,486
  • 12
  • 49
  • 67
  • @user12556559 Consider upvoting and accepting my answer if it worked for you – CinCout Dec 18 '19 at 06:43
  • 2
    But isn't this just a nice writen if-else? I thought he wanted to use the bool statement as numbers with `true`being cast to `1` and `false` being cast to `0`? –  Dec 18 '19 at 07:15
  • @generic_opto_guy: That's exactly what I was asking for. Matlab user here. I find C++ too cumbersome. For some reason, I am trying to convert my matlab scripts into C++. Please help me if you have any better suggestions. – user12556559 Dec 18 '19 at 07:26
  • 1
    @generic_opto_guy I have the same feeling. In such a case, the first iteration can be simply resolved out of the loop and then, no branching is needed. However, if one cares about performance, he/she would definitely not represent a 2D matrix by an _array of arrays_, but use a 1D array instead. – Daniel Langr Dec 18 '19 at 07:32
  • 1
    @user12556559 If what you were asking for is different that this answer, then don't accept it. Accepted answer means that it answers what you were asking for. – Daniel Langr Dec 18 '19 at 07:35
  • @DanielsaysreinstateMonica, New to this platform. Thanks. I am still looking for better alternatives – user12556559 Dec 18 '19 at 07:52
  • @user12556559 Fordyne's answer showed the solution without branching. However, the best production approach would be to store matrix elements in a 1D array (look, e.g., [here](https://stackoverflow.com/q/2151084/580083)). This avoids multiple allocations and enables better cache utilization, prefetching, and SIMD processing. I would recommend using some library, such as [Eigen](http://eigen.tuxfamily.org/). – Daniel Langr Dec 18 '19 at 10:11
2

The line

angle[j][0] = (j == 0) * 2 + (j != 0) * (angle[j-1][0] + 3);

does not work since you access angle[-1] when j is 0. That is a reason for undefined behavior.

Looking at your comment to the other answer, you are apparently looking for using the conditional operator.

angle[j][0] = (j == 0) ? 2 : (angle[j-1][0] + 3);
R Sahu
  • 204,454
  • 14
  • 159
  • 270
2

As Sahu said, the problem with your combined line is that you take angle[j-1][0] with j==0, which is undefined behavior. This means that combining both if and else parts into a single non-branching statement is not really possible.

Secondly, these two code snippets look different (with the ternary/conditional operator producing fewer lines of C++ code):

    if (x == 1) 
        A = 7;
    else
        A = 13;

versus

    A = (x == 1) ? 7 : 13;

But they compile to exactly the same machine code.

So, how do we fix your problem of not wanting to branch in every single loop iteration?

Since the test/branch variable (j) is also the loop variable and the test is for j == 0, which is also the starting condition, you can do something like this:

double** angle = new double* [10];   // for a 2D matrix

angle[0] = new double[3];  // Prepare the first element
angle[0][0] = 2;

for (int j = 1; j < 10; j++)  // Fill out the rest
{
    angle[j] = new double[3];
    angle[j][0] = angle[j - 1][0] + 3;

    std::cout << angle[j][0] << std::endl;
}

Where the setup for the first element is moved out of the loop, after which the loop can be started at j=1 and only have the else branch in the loop body (with no test, of course).

However, given that branch predictors in modern CPUs are pretty awesome, and your loop hits the if-branch exactly once; on the first loop-iteration, and the else-branch on every following one, I doubt that you will see much difference in the execution times for the two versions. So, I would simply recommend that you pick the version that you find most easy to read and understand.

Frodyne
  • 3,547
  • 6
  • 16