The Ternary Operator Revisited

Several years ago, I wrote a blog post entitled Ternary Operator vs. if/else, which was supposed to be an interesting comparison between the two, except that I totally botched it! The premise is that the ternary operator and if/else statements should compile into identical, so why not just use if/else statements to make the code clearer. After all, statements using the ternary operator are cryptic, error-prone, and ugly. So, in this post, I hope to redeem myself and take a proper look at whether we should use the ternary operator or an if/else statement.

Ternary vs. if/else

It’s not uncommon when I am consulting or teaching an embedded systems class to be asked whether the ternary operator should be used or an if/else statement. Hidden behind the question is really the need to know whether the ternary operator is more efficient than if/else. One might think that compilers today would generate identical code. Let’s take a quick look at a simple comparison and see if we can discern the differences between the two.

To compare the two, we will look at the instructions generated by the compiler for an embedded target that is looking to assign the value 0 or 1 to a variable named bitStatus depending on whether the 0x10 bit is set in a variable named bits. But, first, let’s look at each solution and compare them with the compiler optimizer turned off.

Code Generated by the Ternary Operator

The code to perform the desired operation using the ternary operator can be seen below:

bitStatus = ((bits & 0x10) == 0x10) ? 0:1;

When I compile and run the code using MPLAB X, XC32 version 4.0 compiler, I find that the compiler generates the following instructions to perform the operation:

! bitStatus = ((bits & 0x10) == 0x10) ? 0:1;
0x6C32: ADD.W R3, R7, #15
0x6C36: LDRB R3, [R3]
0x6C38: UXTB R3, R3
0x6C3A: AND.W R3, R3, #16
0x6C3E: CMP R3, #16
0x6C40: ITE NE
0x6C42: MOVNE R3, #1
0x6C44: MOVEQ R3, #0
0x6C46: UXTB R3, R3
0x6C48: STRB.W R3, [R7, #559]

Overall, the compiler has generated ten instructions to operate.

Code Generated by if/else.

The code to perform the desired operation using an if/else statement can be found below:

    if((bits & 0x10) == 0x10)
    {
        bitStatus = 0;
    }
    else
    {
        bitStatus = 1;
    }

When I compile and run the code using MPLAB X, XC32 version 4.0 compiler, I find that the compiler generates the following instructions to perform the operation:

! if((bits & 0x10) == 0x10)
0x6972: ADD.W R3, R7, #15
0x6976: LDRB R3, [R3]
0x6978: UXTB R3, R3
0x697A: AND.W R3, R3, #16
0x697E: CMP R3, #16
0x6980: BNE.N 0x698B
! {
! bitStatus = 0;
0x6982: MOVS R3, #0
0x6984: STRB.W R3, [R7, #559]
0x6988: B.N 0x6991
! }
! else
! {
! bitStatus = 1;
0x698A: MOVS R3, #1
0x698C: STRB.W R3, [R7, #559]
! }

There are 11 instructions necessary in the if/else statement.

What are the Results?

From a glance, one might suggest that using the ternary operator is more efficient. Without optimizations enabled, the ternary operator uses one less instruction, ten vs. 11. Does that close the case? I don’t think so. Let’s examine these results a little closer.

The ternary operator will save a single instruction peruse, which means that using the ternary operator will likely decrease the amount of flash memory used by the application. Given that it is just a single instruction and that most of our applications have hundreds of kilobytes available, it’s probably not a big saving, but still worth considering.

If we view the differences from an execution standpoint, the story is slightly different. If one were to actually follow the executable path through the if/else statement, for a given result, the if/else statement actually has fewer instructions to execute compared to the ternary operator. Ternary operator requires ten instructions, while the if/else statement will execute 8 or 9 instructions depending on whether bitStatus is set or cleared. The if/else code will run faster by 1 – 2 instructions. Given that we run at 10’s to 100’s of MHz, it’s not that big of savings.

Does the Optimizer Help?

What gets even more interesting is that if we start to bump up the optimization level, I found that at optimization level 2, both operations were identical at two instructions. This might have been because my example was trivial, but I did my best to force the compiler to generate real code.

Conclusions

So which do you use, ternary or the if/else statement?

We can use both the ternary operator and if/else statement to get equivalent results in our code. Many developers are tempted to believe that the ternary operator is more efficient, and in some situations and with certain compilers and optimization settings, it very well can be. However, reading a ternary statement can often be more confusing and error-prone than if/else statements. The savings from using the ternary operator from a size point is trivial. The if/else speed boot is also trivial.

I always use if/else over the ternary operator simply due to the difference in code readability. I want fewer mistakes and more readable code. However, the ternary operation can be written on a single line is not justification for using it. From this quick look under the hood at what the compiler is doing, we can see that there doesn’t necessarily have to be a performance or code size hit to use if/else statements.

4 thoughts on “The Ternary Operator Revisited”

  1. I like your presentation.

    When thinking about why would one be used over the other, you covered the possibilities well. Notice however that the optimizing compiler used another approach to get simplification. It is as if the real problem was bit masking to expose a value for a single bit of zero or one. For single bits, either ternary or if/else works but is far too complex. For multiple bits, a case statement works out better.

  2. Another difference in the example is that in the first case with the ternary operator, there were no branches in the generated code. This has two effects: (1) The instruction cache is unaffected so that the ternary code might actually be slightly more execution time efficient than the if-then-else code that has branches that can affect the instruction pipeline and (2) If you are trying to debug this code to determine what is going on, there is no place to set a simple breakpoint with the ternary code while there is with the if-then-else. Of course there are other ways of setting a breakpoint that can be used, but they are not as straight forward.

    And note that in this code example, “bitstatus” is actually the inverse of the bit value in the original variable “bits”. Therefore “invertedbitstatus” might be a better name than “bitstatus”. Of course in real code, this would not be as obvious since none of the masks would be presented as magic numbers.

    If efficiency WERE paramount, the following code might achieve a more efficient result, but in all likelihood, the optimized generated code would not be any better and this code is far harder to understand and maintain:
    actualbitstatus = (bits >> 4) & 0x01;
    or
    invertedbitstatus = ((bits >> 4) & 0x01) ^ 0x01;

  3. The ternary operator helps ensure that any conditional that must always handle both conditions in order to make logical sense does so. The Else branch could have been forgotten about and it would still compile and possibly not get caught in a code review. Being more concise, it lends itself to cleaner code, but it can be a pain to debug.

    1. Thanks for the comment! (I completely disagree that it’s cleaner code, but to each his own!)

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.