5

When I have an integer or a pointer a, it turns out that both

!a

and

a==0

have the same behavior. At a very low level, is there some difference in terms of speed of computation?

the_candyman
  • 1,563
  • 4
  • 22
  • 36
  • 4
    It shouldn't, but check the generated assembly code. – Fred Foo May 22 '14 at 08:06
  • 4
    Most probably no. And if there is, why would it matter? – The Paramagnetic Croissant May 22 '14 at 08:06
  • 7
    *premature optimization is the root of all evil*. – dandan78 May 22 '14 at 08:07
  • It mostly depends on compiler and even more on used compiler optimization flags. – Arsenii Fomin May 22 '14 at 08:09
  • for information only: you could ask the same question about the outcome of `bool(a)` and `!!a` or the difference between `a >= 0` and `!(a < 0)`. These are also logically equivalent and the equivalence is used extensively in the standard library. Optimising compilers compare about logical outcomes, not the code you write :-) – Richard Hodges May 22 '14 at 08:13
  • 3
    @danda78, so is writing that worn out line regardless of the original question. The question doesn't have to be about optimizing some code, but trying to get a better understanding performance difference of similar (possible identical) operations – Neowizard May 22 '14 at 08:39
  • @Neowizard the point is that it isn't. For this code the compiler will select whichever assembly instructions it thinks is best. If you want to ask about a particular assembly instruction on a particular platform, that would be a different question. – M.M May 22 '14 at 09:48
  • A C compiler might (of optimizations are disabled, I'm not sure). A compiler for C++ is a different story, and might be worth discussing. In any case, as long as the question wasn't already answered, there is room for some (perhaps a little) discussion, and calling "premature optimization" prematurely hinders that discussion. – Neowizard May 22 '14 at 10:14
  • My question is for better understanding how a compiler works. – the_candyman May 26 '14 at 18:21

7 Answers7

10

It's unlikely there will be a difference in performance, because compilers know about the logical equivalence and so there's no reason they can't emit identical code for both.

The equivalence is elementary, not some clever theorem: the meaning of !a for integer types defined in the standards is "value of a equal to 0" (or strictly speaking as James points out, "not (value of a not 0)"), and the meaning of !a for pointer types is "a is a null pointer" (or strictly speaking "not (value of a is a non-null pointer)".

However, there's no requirement that a compiler must emit identical code for both, so it's not guaranteed by the C or C++ standard that performance is identical.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • The same thing holds for floating point types as well. And for unscoped enums and pointers to member. Basically, for any type `T` which has an implicit conversion to `bool`, that conversion is defined as `a != static_cast( 0 )`. (The one exception is `std::nullptr_t`, which always converts to `false`; I'm not even sure that `static_cast( 0 )` is legal.) – James Kanze May 22 '14 at 08:18
  • "However, there's no requirement that a compiler must emit identical code for both". But there is, see C11 6.5.3.3/5. – Lundin May 22 '14 at 09:17
  • @Lundin: do you mean, "The result of the logical negation operator ! is 0 if the value of its operand compares unequal to 0, 1 if the value of its operand compares equal to 0."? That only says the result is the same. It says nothing about performance and it does not imply that the emitted code must be identical, only that it's equivalent to the extent that it produces the same result. – Steve Jessop May 22 '14 at 09:20
  • I mean `"The expression !E is equivalent to (0==E)."` which leaves no room for personal interpretation. If the compiler generates different code for each case, which results in different execution speed, then ! and == aren't equal and therefore the compiler isn't standard compliant. – Lundin May 22 '14 at 09:23
  • 1
    @Lundin: I believe that you are mistaken as to what the word "equivalent" means in the C standard. All it implies is that the defined behavior of the abstract machine is the same. It does not imply that emitted code is identical or that performance is the same, since neither of those things is part of the abstract machine whose behavior is defined by the standard. – Steve Jessop May 22 '14 at 09:25
  • 1
    For that matter, look at the previous clause /4, "If the promoted type is an unsigned type, the expression ~E is equivalent to the maximum value representable in that type minus E". I don't believe the C standard requires here that `~E` *must* be implemented using a subtract opcode. Or even that it requires if `~E` uses a bitwise negation opcode, then `UINT_MAX - E` must also use a bitwise negation opcode to preserve equivalence of the two. – Steve Jessop May 22 '14 at 09:27
  • No, neither the C standard or a general ISO standard defines a special meaning for the word "equivalent". The meaning is the same as in the English language. As for the ~ case, the standard only mentions the value, not the way to obtain that value. I think this is more of a similar case to `a[i]` must equal `*(a+i)`, which is one of those things in the standard that all compilers follow to the letter. – Lundin May 22 '14 at 09:31
  • I don't see what distinction you're trying to draw between "The expression ~E is equivalent to the maximum value representable in that type minus E " and "The expression !E is equivalent to (0==E)". Why do you say that the latter requires the values be obtained the same way, and the former does not? I just don't see your argument at all, I'm sorry. It's not even clear what "the maximum value" here is that it would equivalent to, since the performance of (constant - variable) need not be the same as (variable - variable) involving the same value, although I would say they're equivalent. – Steve Jessop May 22 '14 at 09:34
  • @Lundin: anyway, I think it's clear we cannot reach agreement here. Even if I found examples in the standard of expressions clearly stated to be equivalent and where all common compilers emit different code, you would just assert that all common compilers are non-conforming in this respect. I assert without proof that no compiler-writer interprets the standard as you do, and that neither do its authors. The best I can do is what I've already said that you appear to disagree with: since the standard only defines behavior of the abstract machine, equivalence refers only to that behavior. – Steve Jessop May 22 '14 at 09:37
  • The latter doesn't mention values, the former does. Anyway, in the real world outside the ISO standard, it seems rather unlikely that a compiler would generate different code for the two cases just as you say in your answer – Lundin May 22 '14 at 09:49
  • @Lundin: Compilers have been known to generate different code for `a==0` and `a==0`. Yes, that's literally the same expression - but the (omitted) context will matter. So you can't say that `a==0` must result in the same code as `!a`, either. – MSalters May 22 '14 at 10:09
  • @MSalters: well, the standard presumably *could* define these expression equivalences in terms of a textual substitution, and further it could state if two programs are identical after all such substitutions (that is to say if they're "equivalent" in this sense before it) then they must emit the same object code. Then it would be OK for `a==0` to emit different code in different contexts provided that `!a` in each of those contexts emits the same code as `a==0` in that context. But it doesn't do so because that would be a waste of everyone's time and energy :-) – Steve Jessop May 22 '14 at 10:30
9

By definition, ! requires an operand of type bool; if its operand has a type int, then there is an implicit conversion (which is nice for obfuscation, but should generally be avoided otherwise). By definition, the implicit conversion of int a to bool is a != 0. So !a is !(a != 0). In practice, it's hard to imagine any compiler not generating exactly the same code for !(a != 0) as for a == 0, so performance certainly shouldn't be a consideration. Say what you mean and mean what you say: since you are comparing to 0, that's the way it should be written.

EDIT:

As Steve Jessop points out, if the type being compared is not int, the formal definition of !a is !(a != static_cast<T>( 0 ), where T is the type of a. And while implicit conversions generally hurt readability, I don't think anyone would insist on an explicit conversion of 0 to another integral type. On the other hand, I systematically write a == 0.0 for floating point types, and in C++, a == nullptr for pointer types—which involves another implicit conversion, from std::nullptr_t to the pointer type. Not all implicit conversions are equal:-). (FWIW: I'd accept implicit conversions between integral types, and between floating point types as long as they are not narrowing, as defined in C++11 §8.5.4/7, except that '\0' should be used, instead of 0, for character data; and implicit conversions between pointers, and between a pointer and an std::nullptr_t. Implicit conversions of integral types to floating point types don't bother me either, although I'd never write them. But that's about it.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • FWIW, if the operand has any type other than `int` then there's also an implicit conversion in `a != 0`. So, if your goal is to avoid implicit conversions then you make the type on the RHS match the LHS, whatever that may be. For longs, `a != 0L`. For pointers `a != static_cast(nullptr)`, which I hope nobody would really write ;-) – Steve Jessop May 22 '14 at 08:15
  • 3
    +1 for _say what you mean_: If the purpose of the check is to determine whether a numerical value equals 0, `== 0` is the right thing. If the purpose of the check is to determine some abstract condition being unfulfilled (which just happens to be modeled through a numeric value), use `!`. Consider: `if(numberOfItems == 0)` and `if(!file_handle)`. – ComicSansMS May 22 '14 at 08:16
  • 1
    @ComicSansMS A file handle isn't abstractly true or false either, so it should be `if ( file_handle != invalid_file_handle )`, where `invalid_file_handle` is a defined constant. Although there are contexts... Anyone programming in a Unix environment will feel at home with `if ( fd < 0 )`, for example, without defining some special symbol for `0`, and if the environment defines handles as pointer types (as part of the public interface---a lot of interfaces will use `void*` as a magic cookie in the interface), then `if ( handle != nullptr )` is OK. – James Kanze May 22 '14 at 08:37
  • @JamesKanze Point taken, my example is far from perfect. As you mentioned, in the end it is all about contexts. Thanks for taking the time to clarify. – ComicSansMS May 22 '14 at 08:45
  • Please note that this answer only applies to C++. – Lundin May 22 '14 at 09:15
  • 1
    @Lundin Yes and no. When writing C, you should write it as if C had a boolean type, with more or less the same behavior as the C++ `bool`. But the formal distinctions at the language level are different. – James Kanze May 22 '14 at 09:46
  • 2
    @ComicSansMS Yes. Context is everything. Immediately after posting my comment, it occurred to me that I'm always insisting on people using things like `while ( std::getline( input, line ) )...`, which contains some pretty horrible implicit conversions, if you think about it. But you don't think about it, because `std::istream` is specified in that way: it is `false` if anything fails, `true` otherwise. – James Kanze May 22 '14 at 09:49
3

In C there is no difference and both versions will most likely generate the same machine code. The difference is stylistic.

It is even written plainly in the C standard itself (C11 6.5.3.3):

The expression !E is equivalent to (0==E).

! should only be used on operands that are effectively boolean. Unfortunately, there exists no true bool type in C, everything is evaluated as ints. (Even though C99 introduced a _Bool type and a bool macro, these will still get promoted to integers whenever used in an expression.) Still you should treat expressions as if a bool type existed. Good C programmers write code that pretends that a real bool type exists. (For more information about "effectively boolean", see MISRA-C:2012 Appendix D "Essential types".)

For all other operands, == 0 should be used. This includes plain integers and pointers. For example, writing if(!ptr) is considered bad style, the proper way to check a pointer is if(ptr == NULL). (MISRA-C:2004 12.6)

For similar reasons, if(ptr) is regarded as sloppy coding. Because if expects an expression which is effectively boolean. Proper code uses if(ptr != NULL), which is also more readable and self-documenting. Even though the generated machine code will be identical. (MISRA-C:2004 13.2)

C++ is different, because it has a true bool type. The operand to ! is expected to be bool, and if it isn't a bool, it will get converted to one. C will do the opposite, if the operand is bool, it will get converted to an int. Still, this will not affect performance in the end, because the compiler is allowed to optimize all implicit conversions to the most suitable type, given that it doesn't change the outcome of the code.

Similarly in C++, the result type of ! and == is always a bool, while the result in C is always an int (which you should regard as a bool... but it is still an int).

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Equivalence doesn't mean the performance is same and there's no guarantee that the same machine code will be generated. It may very well be the case in this example. But saying `x+y` is equivalent to `y+x` can't say anything about performance. Performance guarantees are outside the scope of language standard. – P.P May 22 '14 at 09:46
  • @SteveJessop Alright, I'll remove the part about performance, as there seems to be formal no proof for either argument. – Lundin May 22 '14 at 09:51
  • @Lundin you should clarify that everything after your standard quote is just opinion about coding style (as opposed to a performance or correctness issue) – M.M May 22 '14 at 09:55
  • @MattMcNabb It's not just an opinion and it is not just coding style. I'll edit and cite a widely-recognized authority, which is not concerned about coding style, but believes that not doing as advised here is dangerous practice which leads to bugs. – Lundin May 22 '14 at 10:01
  • 1
    @Lundin "dangerous practice which leads to bugs" = a coding style issue. If there is not actually a bug in the code you have written, then it is not a correctness issue. – M.M May 22 '14 at 10:03
  • also, use of `NULL` is discouraged in C++11 – M.M May 22 '14 at 10:04
  • @MattMcNabb Among the sources that MISRA-C is based on, there is various research by computer scientists done in the 90s. Population studies on random C code containing bugs showed that various mechanisms in the C language lead to bugs. Whenever you confront MISRA about things like these and say "this is just coding style, even though you claim that your guidelines aren't concerned with coding style", they will disagree and say that it is known dangerous practice. – Lundin May 22 '14 at 10:09
  • For example, Apple's "goto fail" bug was mainly caused by poor coding style... coding style is far from harmless personal preference. – Lundin May 22 '14 at 10:13
  • 2
    MISRA-C is exactly a set of coding style rules and guidelines. I'm not suggesting that [coding style](http://en.wikipedia.org/wiki/Coding_style) is "just" "harmless personal preference". See link for details. Also, people who don't follow MISRA are not therefore writing bad code. – M.M May 22 '14 at 10:16
  • @MattMcNabb People who follow no coding standard and just hack away according to personal preference, are almost certainly writing bad code. Despite popular belief, there exists many cases in C where there is only one correct way to write code, even though the language allows multiple ones. – Lundin May 22 '14 at 10:21
  • Similarly, if your work is building bridges, you can make them in concrete and steel which is proven good by scientific research, theoretic calculations and experience. Or you can ignore all of that and make bridges out of paper, because you think it looks better and it goes quicker to build them that way. And you had no previous problems with paper bridges personally, so therefore you believe they are fine. Programming is engineering just as building bridges. It is not art. Personal "artistic preferences" are irrelevant. – Lundin May 22 '14 at 10:23
  • 1
    @Lundin what's any of that got to do with this post? I never suggested to not follow a coding standard, not even remotely. So why are you talking to me condescendingly, as if I need education about the value of a coding standard? – M.M May 22 '14 at 10:28
2

I expect no difference in the speed of computation of !a and a==0 for integers and pointers.

Use the variant that most clearly expresses your intention. With integers I would always use `a==0'. With pointers both versions are fine to me.

PS: A performance difference is not impossible, however it should be very small. For good optimizing compilers, a difference is practically impossible. It would be regarded as a bug and fixed soon.

Peter G.
  • 14,786
  • 7
  • 57
  • 75
2

They are the same in many situations. Any modern compiler will choose the best instruction according to the target machine. The first one is popular between developers.

You should consider that it can depend on the type of a in case of overloading operators == and !. (e.g. custom object)

masoud
  • 55,379
  • 16
  • 141
  • 208
2

the best optimization that you can perform, considering just this 2 statements, is to write 0 == a instead of a == 0.

There is a small difference in C, with versions of C that do not offer the bool type, tipically in this old revisions of C bool is being handled as an integer , so the implicit conversion is just to an integer not to a bool, this is where the trick !!a with the double negation comes into place, and that's why this trick is useless in modern versions of the language but largely adopted on old code bases .

Other than that, I can't recall substantial differences.

user2485710
  • 9,451
  • 13
  • 58
  • 102
  • 4
    Why `0 == a` instead of `a == 0`? It just confuses the reader. – James Kanze May 22 '14 at 08:19
  • 1
    @JamesKanze I personally think just the opposite. http://stackoverflow.com/questions/3757941/yoda-conditions-and-integer-promotion#comment3979283_3759466 – user2485710 May 22 '14 at 08:22
  • 1
    @JamesKanze: certainly it confuses readers who don't intuit that `==` is a symmetric relation ;-) It's natural not to intuit that, since in English although "is the same as" is symmetric in literal meaning, it isn't symmetric in emphasis. Therefore when the emphasis is important to comprehending the code, conditionals with the constant on the LHS trip the reader up. Where the emphasis isn't important they (AFAIK) don't. – Steve Jessop May 22 '14 at 08:22
  • Use 0==a to ensure the compiler catches the most common typo here of 0=a. If you do a==0 and make that typo, you can easily end up with an accidental assignment rather than comparison (especially if you ignore compiler warnings). – mc110 May 22 '14 at 08:24
  • 2
    @mc110 I find the "most common typo" to be extremely rare. It does happen, but most compilers will warn in such cases, and because its a well know error, it gets spotted in code review. On the other hand, most people implicitly put the constant on the right when comparing, and expect to see it there when reading. I don't know why, but that's the way it is. So that's what you should do when writing code; anything unusual causes the reader to hesitate, and slows down comprehension. – James Kanze May 22 '14 at 08:41
  • @SteveJessop I'm not sure that it's a question of emphasis or symmetry (although of course, if the relation weren't symmetric, the question wouldn't come up). I think it's more a question of conventions, and what one would write if it weren't a program (or if assignment were a statement, and couldn't be written in a conditional). – James Kanze May 22 '14 at 08:44
  • 3
    -1 for incorrect answer. There is no difference in terms of performance nor type conversions between `0==a` and `a==0`. Also "There is a small difference in C, with versions of C that do not offer the bool type, tipically in this old revisions of C bool is being handled as an integer" is just nonsense, all versions of C handles bool as an integer and all operators in C will promote bool to integer (C++ is different). There exists no implicit conversion in C that will turn anything to a bool. – Lundin May 22 '14 at 08:53
  • @SteveJessop thanks for posting that explanation, it's something that niggles you but you're not sure why... until now :) – M.M May 22 '14 at 09:50
  • @MattMcNabb: and James's point about "if it wasn't a program" is very relevant: you'd normally say "my bank balance is zero" because that suggests/emphasises that the first (variable) thing has taken on the value of the second (constant) thing. It's grammatically correct to say "zero is my bank balance", you just normally wouldn't unless perhaps everyone was talking about bank balances already and you wanted to say in effect "*zero*, that's what my bank balence is", or if you were talking about things zero is and used bank balance as an example. Which is why I put it in terms of emphasis :-) – Steve Jessop May 22 '14 at 10:01
  • @Lundin explain the double negation trick with your own words. My remark on the differences was obviously related to the primitive types of the language, not to the evaluation of a specific `if` . – user2485710 May 22 '14 at 10:34
  • @SteveJessop well, `std::move` doesn't move anything and a "2D matrix" `matrix[][]` is just a plain old 1D array of values from both the machine and compiler perspective . A programming language usually _tries_ to be more idiomatic with a simple syntax and semantic, but most of the times, what you can deduce just by looking at what you write has almost nothing to do to what really happens. I would rather accept this kind of tradeoffs in my code than think about this kind of techniques that are pointless for something that is designed to work on a machine and not to communicate with humans. – user2485710 May 22 '14 at 10:38
  • @user2485710 Except that `matrix[][]` _isn't_ just a plain old 1D array, at least to the compiler. Actually, `matrix[][]` is not a legal type in C++. All but the last dimension must be specified with a compile time constant. But `matrix[10][10]` is very different from `matrix[100]`. – James Kanze May 22 '14 at 11:08
2

I always us !a in my own code, but only because it's shorter/simpler (at work, the coding standard says 0 == a).

As others have said, the compiler will sort out any difference, and either case it will perform some sort of check "is the value zero or not" and form a result that is a boolean from that. The end result should be very much the same, either way.

The above is assuming the type of a is a simple type (integer/char/bool or floating point type). If it happens to be a class or struct, then the !a version will call the operator bool operand, where the a == 0 will probably use a different approach, e.g. operator== - note that the rules here are complex, and the effect of one over the other can be substantial.

In summary, for simple types, it's just a matter of style (in most compilers!). For complex types, it depends on the design of the class/struct involved.

And if performance really matter, measure the difference between different solutions, and see which performs best in that case.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227