Why ~(true^true) is not true? Boolean operators (negation) works for `unsigned char`s, but not for bools? (C++)
Asked Answered
C

5

5

I heard that often "everything" besides 0 is true. But now very strange things are happening to me... or I just think that I do it in correct way while I don't. Here's what is happening:

When I want to check if a is equivalent b, I can use NOT(a XOR b). When I checked it for unsigned char's, everything was ok, for example

unsigned char a = 5;
unsigned char b = 3;
unsigned char c = ~(a^b);

gave me c == 249:

a is: 00000101, which is 5.

b is: 00000011, which is 3.

~(a^b) is: 11111001, which is 249.

Now, let's try this with bool's.

cout << ~(true^true) << ~(true^false) << ~(false^true) << ~(false^false) << endl;
cout << ~(~(true^true)) << ~(~(true^false)) << ~(~(false^true)) << ~(~(false^false)) << endl;

if (~(true^true) == true)
    cout << "true";
else
    cout << "false";

This gives me in console:

-1-2-2-1
0110
false

while I expected the first line to be:

1001

After asking a friend, he advised me to try ! instead of ~ and see if it will work correctly. And (I think) it works correctly now. But I don't understand why. Shouldn't boolean negation work for bools?

Coactive answered 18/5, 2014 at 21:22 Comment(7)
Boolean negation does work for booleans. It's bitwise-NOT that doesn't.Accountable
A bool has more than one bit. ~ flips each bit.Interventionist
It is true, it's just not equal to true.Wee
How many types of negations we have? I was using this page: cplusplus.com/doc/boolean and there is only one mentioned.Coactive
Ok, I found this: cplusplus.com/doc/tutorial/operators. But why bit-wise don't work and I have -1? Isn't 0 or false represented in this case as 00000000 ?Coactive
@Coactive Kerrek got a nice answer, you should definately read it.Wahkuna
Thank you, I think I got it now :)Coactive
S
4

I heard that often "everything" besides 0 is true

It is valid for conditions as for example in the if statement (here and below I am citing the C++Standard)

The value of a condition that is an expression is the value of the expression, contextually converted to bool for statements other than switch

For example if you would write as

if (~(true^true) )
    cout << "true";
else
    cout << "false";

instead of your code snippet

if (~(true^true) == true)
    cout << "true";
else
    cout << "false";

or when the logical negation operator ! is used

9 The operand of the logical negation operator ! is contextually converted to bool (Clause 4); its value is true if the converted operand is false and false otherwise. The type of the result is bool

As for operator == then

6 If both operands are of arithmetic or enumeration type, the usual arithmetic conversions are performed on both operands; each of the operators shall yield true if the specified relationship is true and false if it is false.

That is in case of

if (~(true^true) == true)

the usual arithmetic conversion is applied that is boolean value true is converted to integer value 1 and it is not equal to the expression of the left operanf because internal binary representation of the left operand differs from 1 as showed the output of your first code snippet..

Salchunas answered 18/5, 2014 at 21:38 Comment(2)
Great answer, now I understand much clearly what really is happening, thank you! Where I can find standard you are citing? I get a little confused while searching it in google.Coactive
@Coactive You can find the Working Draft of the Standard at open-std.org/jtc1/sc22/wg21Salchunas
A
9

You're misunderestimating the arithmetic conversions. When you say ~e for some expression e of integral type, the value is first promoted to at-least-int, and same for e1 ^ e2 (and for any arithmetic expressions, for that matter). So true ^ true first has its operands promoted to int, yielding 1 ^ 1, which is indeed 0, and thus you end up with ~0, which on your platform is -1.

You can vaguely make sense of your operation by converting the result back to bool:

std::cout << static_cast<bool>(~(true ^ true))

In your final problem, since you have an expression with the == operator where the two operands have different types, both operands are promoted to the common type, which is again int, and -1 is different from 1. Again, you can convert both operands to bool first to get the desired equality.


The meta lesson is that the built-in operators in C++ acting on integers really only operate on int and wider, but not on any of the shorter types (bool, char, short) (and similar considerations apply to passing integers through an ellipsis). While this may cause some confusion at times, it also simplifies the language rules a bit, I suppose. This is all part of the C heritage of C++.

Accountable answered 18/5, 2014 at 21:26 Comment(2)
"Misunderestimating" ? Is that a word, or did a joke just fly over my head ?Disequilibrium
@Quentin: Can't it be both? :-)Accountable
S
4

I heard that often "everything" besides 0 is true

It is valid for conditions as for example in the if statement (here and below I am citing the C++Standard)

The value of a condition that is an expression is the value of the expression, contextually converted to bool for statements other than switch

For example if you would write as

if (~(true^true) )
    cout << "true";
else
    cout << "false";

instead of your code snippet

if (~(true^true) == true)
    cout << "true";
else
    cout << "false";

or when the logical negation operator ! is used

9 The operand of the logical negation operator ! is contextually converted to bool (Clause 4); its value is true if the converted operand is false and false otherwise. The type of the result is bool

As for operator == then

6 If both operands are of arithmetic or enumeration type, the usual arithmetic conversions are performed on both operands; each of the operators shall yield true if the specified relationship is true and false if it is false.

That is in case of

if (~(true^true) == true)

the usual arithmetic conversion is applied that is boolean value true is converted to integer value 1 and it is not equal to the expression of the left operanf because internal binary representation of the left operand differs from 1 as showed the output of your first code snippet..

Salchunas answered 18/5, 2014 at 21:38 Comment(2)
Great answer, now I understand much clearly what really is happening, thank you! Where I can find standard you are citing? I get a little confused while searching it in google.Coactive
@Coactive You can find the Working Draft of the Standard at open-std.org/jtc1/sc22/wg21Salchunas
A
3

Considering your first example only,

~(true^true)

First, the operands of ^ are promoted to int, so that this is equivalent to

~(1^1)

Then xor of 1 with itself yields an all-zero bitpattern, which regardless of int representation denotes the value 0:

~0

This inverts every bit in the all-zeroes bitpattern for 0, resulting in a bitpattern with all bits 1, e.g. for 32-bit int,

int( 0xFFFFFFFF )

With the now almost universal two's complement form representation of signed integers this is then the value -1.

Your output is therefore

-1
Antipole answered 18/5, 2014 at 21:49 Comment(0)
H
2

~ is a bit-wise NOT.

e.g. ~0 is 255 for unsigned char, and 255 != true.

Hectare answered 18/5, 2014 at 21:25 Comment(0)
C
2

There is a difference between logical negation and bitwise negation. bitwise negation flips the bits in variable, and logical negation flips the logical value, i.e. true/false. Consider for example the number 5, represented in binary as 101. If you flip just the bits (~5) you would get 11111010, which is not false, because only all zeros is false in c++. But if you compute (!5) you would get all zeros, which would work inside a condition.

Cousins answered 18/5, 2014 at 21:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.