Confusion while evaluating conditions with more than one logical NOT operator - c++

I'm confused when code includes more than one NOT operator like:
if (!x != !0)
;
or similar. I can read it as: if NOT x is NOT equal to NOT zero,
but in my mind I'm totally confused about what it actually means.
Do you have any advice regarding this? I.e. how to understand such code, where to start reading or etc.
Another example:
if(!x == !1)

You can use truth table if you are not sure. For instance
x | 0 | x!=0 | !x | !0 | !x != !0
0 | 0 | 0 | 1 | 1 | 0
1 | 0 | 1 | 0 | 1 | 1
If have problems with many && and ||, use de Morgan's laws

to make things simpler, evaluate the operator! first then read L->R.
Things to remember:
!0 = 1 // true
!1 = 0 // false
so your condition can be simplified to:
if (!x != true) // !0
if (!x == false) // !1
Now, any non-zero value when inverted will be zero.
int x = 10;
!x // zero
Zero when inverted is true.
int x = 0;
!x // one
In C or C++, true is 1 and false is 0

I got troubled as well with simalar syntax when I started developing on PowerBuilder, then I realized I just need to imagine it as a nested if checking false.
For example !x become if(x)=false so it makes more clear that is true when x is false or zero.
In C 0 is false, whatever is not zero is true.
In the same logic !1 is always false and !0 is always true, despite I cannot see the reason to type it in this confusing way, maybe the code you are looking at is coming out from a sort of automatic generator / converter.

First take a look at operator precedence. You will see that logical operators like ! take precedence over relational operators like !=.
Secondly, what is !0 - this suspiciously sounds like there is an implicit conversion from int to bool there - otherwise !0 would make no sense at all.
In your example, you need to evaluate the logical operators first (i.e. !x and !0), then check if they are not equal !=. That said, this kind of code is really bad as it is really hard to read - avoid writing code like this, if possible (and consider refactoring it - while covered by unit tests - if you encounter it in "the wild")

I assume that the question is about C, and therefore I will give the answer in C.
You need to know the precedence rules - the ! binds stronger than the !=. Therefore the expression can be clarified using parentheses as (!x) != (!0). Now, the next thing is to know what ! will do - the result will be 0 if the operand is non-zero, 1 if it is zero. Therefore, !0 can be constant-folded to 1.
Now we have (!x) != 1. Since the !x is 1 iff x is zero, the result of this entire expression will be 1 iff x is non-zero, 0 otherwise.
We can therefore reduce this expression into the more idiomatic double-negation: !!x. However, the if clause in itself tests whether the expression is non-zero, therefore the entire if statement can be changed to if (x) ; (and since the expression does guard only a null-statement, it can be elided altogether)

This kind of code is designed to trick you. Your brain have difficulty to process double-negation or triple negation (and you are not the only one).
You need to know what are the priority rules, and apply it:
(!x == !1) is equal to ((!x) == (!1))
If you see this kind of code during a code review, you should definitely highlight it, and ask for an update.
Please note that in C++ you can also use not instead of !. It can makes things easier to understand:
(!x == !1) is equal to ((not x) == (not 1))

In C and C++, a value of zero is considered "false", and a nonzero value is considered "true". But it can occasionally be confusing, and occasionally causes minor problems, that two different values can both be considered true even though they're, well, different.
For example, suppose you had a function is_rich() that told you whether a person was rich or not. And suppose you wrote code like this:
int harry_is_rich = is_rich("Harry");
int sally_is_rich = is_rich("Sally");
Now the variable harry_is_rich is zero or nonzero according to whether Harry is poor or rich, and sally_is_rich is zero or nonzero according to whether Sally is poor or rich.
Now suppose you're interested in knowing whether Harry and Sally are both rich, or both poor. Your first thought might be
if(harry_is_rich == sally_is_rich)
But wait. Since any nonzero value is considered "true" in C, what if, for some reason, the is_rich() function returned 2 when Harry was rich, and returned 3 when Sally was rich? That wouldn't be a bug, per se -- the is_rich() function perfectly meets its specification of returning nonzero if the person is rich -- but it leads to the situation that you can't write
if(harry_is_rich == sally_is_rich)
(Well, of course you can write it, but like any buggy code, it might not work right.)
So what can you do instead? Well, one possibility is to write
if(!harry_is_rich == !sally_is_rich)
You can read this as "If it's not the case that Harry is rich has the same truth value as not the case that Sally is rich". And, while it's obviously a little contorted, you can kind of convince yourself that it "means" the same thing.
And, although it's a little confusing, it has the advantage of working. It works because of the other confusing aspect of true/false values in C and C++.
Although, as I said, zero is considered false and any nonzero value is considered true, the built-in operators that generate true/false values -- the ones like &&, ||, and ! -- are in fact guaranteed to give you exactly 0 or 1. (That is, the built-in functions are significantly different that functions like is_rich() in this regard. In general, is_rich() might return 2, or 3, or 37 for "true". But &&, ||, and ! are guaranteed to return 1 for true, and 0 for false.)
So when you say !harry_is_rich, you'll get 0 if Harry is rich, and 1 if Harry is not rich. When you say !sally_is_rich, you'll get 0 if Sally is rich, and 1 if Sally is not rich. And if you say
if(!harry_is_rich == !sally_is_rich)
you'll correctly discover whether Harry and Sally are both rich, or both poor, regardless of what values is_rich() chooses to return.
One more thing, though. In all of this I've been considering integers (or other types that might have lots of values). But both C and C++ have bool types, that more succinctly represent true/false values. For a bool type, there are exactly two values, true and false (which are represented as 1 and 0). So for a bool type, you can't have a true value that's anything other than 1. So if the is_rich() function was declared as returning bool, or if you declared your variables as bool, that would be another way of forcing the values to 0/1, and in that case the condition
if(harry_is_rich == sally_is_rich)
would work fine. (Furthermore, when I said that operators like &&, ||, and ! are guaranteed to return exactly 1 or 0, that's basically because these operators don't "return" arbitrary integers, but instead "return" bools.)
See also the C FAQ list, question 9.2.
(Oh, wait. One more thing. The opposite of "rich" isn't necessarily "poor", of course. :-) )

Related

no comparison in if() judgement but seems give a boolean value

if(!word.size()){
return true;
}
whole code screenshot
how here use string.size() lonely to return a boolean value?
I googled the string.size() method although i already know it returns a int value,but here it works like a true/false method;
Lots of things in C++ can be coerced to Booleans. Off the top of my head.
Booleans are trivially convertible to Booleans
Numbers (int or double) can be converted to Boolean; zero is false and anything else is true
Streams (like fstream instances or cout, for instance) can be converted to Boolean and are true if the stream is in "good" condition or false if there's a problem
As indicated in the comments, you shouldn't use this in real code. if (!word.size()) is silly and confusing an should only be seen in code golf challenges. Coding isn't just about making the computer understand what you mean; it's about making sure future readers (including yourself six months down the line) understand as well. And if (word.empty()) conveys the exact same intent to the computer but is much more understandable to the human reader at a glance.
So why does C++ allow this if it's discouraged? Historical reasons, mostly. In the days of C, there was no dedicated bool type. Comparisons returned an integer, and the convention was that 0 meant "false" and anything else (usually 1) meant true. It was the programmer's job to remember which things were Booleans and which were actual integers. C++ came along and (rightly) separated the two, making a special type called bool. But for compatibility with C, they left in the old trick of using integers as Booleans. Successor languages like Java would go on to remove that capability. if (myInteger) is illegal in Java and will give a compiler error.
The language checks if the condition inside the conditional is true or false. In this case, the int value gets converted into a boolean. If the size returns 0 this will get converted to false, any other value will be converted to true.

Why is the return type of isdigit() int instead of boolean?

I was reading the cplusplus reference for the isdigit() function, where I got this,
int isdigit ( int c );
Return Value: A value different from zero (i.e., true) if indeed c is
a decimal digit. Zero (i.e., false) otherwise.
What does this term "different from zero" indicate, I mean why we can't just stick to 0 or 1.
Also when I tested this function, it is always returning either 1 or 0, then why simply documentation can't say that isdigit function returns 1, instead of saying "different from zero".
The reason is cross compatibility with C.
C itself didn't acquire a Boolean type until C99 and while that was over 20 years ago there's actually little justification for changing the definition of a widely used library function.
A explicit Boolean type has little practical advantage over the conventional use of 0 for false and non-zero for true other than readability.
By different from zero it means 'not zero'. It's near universal computing convention that when encoding Boolean values as numerical values 0 represents 'false' and all other values can be used to represent 'true'.
In general that's not something to worry about because constructs like if(isdigit(c)) will work because if() conforms to the same convention (in C and C++).
But if you're writing something like count+=isdigit(c) to perhaps count the number of digits in a string you may not get the answer you want.
As pointed out in the comments implementations may have reason for not returning 1 as true.

Checking null pointer && pointer->member value in the same if statement

This is a pretty basic question, but I could not find a clear answer:
is it allowed to check that a pointer is not null and (&&) to also check one of its members value in the same if statement?
Rephrased: is the right part of the condition in the example below even evaluated if f is null?
I know this works in VS current version, but what I need to know is if this is allowed by the C++ standard (or if it's UB).
Also, if I write it as 2 separate if to make it more readable, can I expect the compiler to optimize it into a single if?
struct foo
{
bool bar;
};
void main(){
foo *f;
// DO THINGS
if (f != null && f->bar == true)
{
// DO THINGS
}
}
Edit: the question is different from this one because it's not obvious that it's simply a matter of order: the proof is I did not end on that SO answer when I googled my question.
...is it allowed to check that a pointer is not null and (&&) to also check one of its members value in the same if statement?
It's perfectly valid, it is not UB, the expression is evaluated from left to right, if the left part of the expression evaluates to false the right part is not evaluated. This is usually called operator short circuit.
The rationale is that if the first part is false, there is no possibilty of the whole expression being true, false && false is false, false && true is also false.
...if I write it as 2 separate if to make it more readable, can I expect the compiler to optimize it into a single if?
In light of the above answer, you wouldn't need two ifs, I would argue that it will not make your code more readable, I prefer the way you have it right know, in any case this is only my opinion. About the compiler, I wouldn't think that there will be much difference either way, as sampled in this live demo.
is it allowed to check that a pointer is not null and (&&) to also check one of its members value in the same if statement?
Yes.
is the right part of the condition in the example below even evaluated if f is null?
No.
what I need to know is if this is allowed by the C++ standard
Yes.
(or if it's UB)
No.
Also, if I write it as 2 separate if to make it more readable, can I expect the compiler to optimize it into a single if?
I would expect it. Seems like a trivial optimisation. Note that in cases where the && operator is overloaded (which never is in the case of pointers), such change can change the meaning of the program.
Standard quote (latest draft):
[expr.log.and]
The && operator groups left-to-right.
The operands are both contextually converted to bool.
The result is true if both operands are true and false otherwise.
Unlike &, && guarantees left-to-right evaluation: the second operand is not evaluated if the first operand is false.

Why doesn't a user defined conditional work?

I am currently studying OCAML, and have a question about a user-defined if-then such as:
let cond (c,t,e) =
match c with
| true -> t
| false -> e
When used in a factorial function:
let rec fact n =
cond (n=0,1, n * fact (n-1))
Intuitively, it seems to be correct, but I know it will throw a stack overflow error. Can someone explain to me why this is, and how this user-defined if-then differs from the builtin if-then?
Basically your user defined conditional is not lazy evaluated. Before the actual match takes place, OCaml tries to evaluate both expressions you pass - for the true and false cases.
Example:
Let's suppose we try to evaluate fact 2.
The return value is the expression cond (2=0,1, 2 * fact (2-1)). Before the 3-tuple is passes to cond, it has to be fully evaluated. To do that Ocaml has to evaluate the function fact (2-1).
Now we evaluate fact 1. The return value is cond (1=0,1, 2 * fact (1-1)). Again, we need to know the value of fact (1-1), so we compute it recursively.
We evaluate fact 0. Here the problem starts to show. The return value is cond (0=0,1, 0 * fact (0-1)), but in order to evaluate the function cond we first have to evaluate its arguments - the 3-tuple. This makes us evaluate fact (0-1)!
Then, we are evaluating fact -1...
... fact -2 ... fact -3 ... and the stack overflows :)
The built-in if-then evaluates its arguments lazily: first, it checks whether the condition is true or false, then it accordingly chooses only one branch to evaluate - this behavior is called lazy evaluation.
Actually OCaml has operations lazy and force you could use to avoid this undesirable behavior, but probably it is better just to stick to traditional if.

C++ short-circuiting of booleans

I'm new to c++ and am curious how the compiler handles lazy evaluation of booleans. For example,
if(A == 1 || B == 2){...}
If A does equal 1, is the B==2 part ever evaluated?
No, the B==2 part is not evaluated. This is called short-circuit evaluation.
Edit: As Robert C. Cartaino rightly points out, if the logical operator is overloaded, short-circuit evaluation does not take place (that having been said, why someone would overload a logical operator is beyond me).
Unless the || operator is overloaded, the second expression will not be evaluated. This is called "short-circuit evaluation."
In the case of logical AND (&&) and logical OR (||), the second expression will not be evaluated if the first expression is sufficient to determine the value of the entire expression.
In the case you described above:
if(A == 1 || B == 2) {...}
...the second expression will not be evaluated because
TRUE || ANYTHING, always evaluates to TRUE.
Likewise,
FALSE && ANYTHING, always evaluates to FALSE, so that condition will also cause a short-circuit evaluation.
A couple of quick notes
Short circuit evaluation will not apply to overloaded && and || operators.
In C++, you are guaranteed that the first expression will be evaluated first. Some languages do not guarantee the order of evaluation and VB doesn't do short-circuit evaluation at all. That's important to know if you are porting code.
The B==2 part is not evaluated.
Be careful! Don't put something like ++B==2 over there!
C++ applies short circuiting to Boolean expression evaluation so, the B == 2 is never evaluated and the compiler may even omit it entirely.
The compiler handles this by generating intermediate jumps. For the following code:
if(A == 1 || B == 2){...}
compiled to pseudo-assembler, might be:
load variable A
compare to constant 1
if equal, jump to L1
load variable B
compare to constant 2
if not equal, jump to L2
L1:
... (complete body of if statement)
L2:
(code after if block goes here)
This is short-circuit evaluation, as James says. Lazy evaluation is something entirely different.
No it's not.
Same with &&, if one is wrong, it doesn't bother evaluating the other one.
B == 2 is never evaluated.
See Short-Circuit Evaluation for more information.