Setting extra bits in a bool makes it true and false at the same timeIs it UB to write values other than 0 or 1 in a bool? If yes, how do they compare?Does the C++ standard allow for an uninitialized bool to crash a program?Signedness aliasing using reinterpret_castboost::lexical_cast wrong outputSince booleans are an integral type in C++, does this mean “0.0” is not false?Does “if ([bool] == true)” require one more step than “if ([bool])”?How to make a boolean variable switch between true and false every time a method is invoked?isalpha(<mychar>) == true evaluates to false?NaN to Bool conversion: True or False?Understanding the bit shifting in C++C# bool operations to detect any false returnWhy does GCC generate 15-20% faster code if I optimize for size instead of speed?Why is f(i = -1, i = -1) undefined behavior?Why does std::foreach not work with a std::vector<bool>?Why Clang at Coliru can't compile vector::push_back?
Is a for loop using arrays better than using field splitting on a simple variable?
Is CR12 too difficult for two level 4 characters?
Why does Bane's stock exchange robbery actually work to bankrupt Bruce Wayne?
What's a good strategy for offering low on a house?
If a tournament advertises G/30, can either player demand G/25 d5?
If a celestial gets corrupted and turns evil, does it get converted to a fiend?
What's the most profitable use for an elemental transmuter?
Is it possible to commute 34 km (21 miles) daily?
What term can we propose for someone who prioritizes equipment over musicianship?
Greek diacritic with English letter
Why are the Ukraine related congressional hearings behind closed doors?
Why does the Joker do this to Bob?
Sci-fi book trilogy about space travel & 'jacking'
Relying on default field initialisation - is bad programming style?
How could a sequence of random dates be generated, given year interval?
What do you call this when cats hunch their backs and their fur stands on end?
Can a trainer send me a gift every day without opening my gift?
Simple code that checks if you're old enough to drive
Why did the people of Zion never find evidence of the previous cycles?
Why do the new Star Trek series have so few episodes in each season?
Is there a high level reason why the inverse square law of gravitation yields periodic orbits without precession?
Artificially isolated pawn in the Caro-Kann
I am particularly fascinated by the Chinese character that is pronounced SHIN & means faith or belief
What does exhaust smell on oil and transmission dipstick mean?
Setting extra bits in a bool makes it true and false at the same time
Is it UB to write values other than 0 or 1 in a bool? If yes, how do they compare?Does the C++ standard allow for an uninitialized bool to crash a program?Signedness aliasing using reinterpret_castboost::lexical_cast wrong outputSince booleans are an integral type in C++, does this mean “0.0” is not false?Does “if ([bool] == true)” require one more step than “if ([bool])”?How to make a boolean variable switch between true and false every time a method is invoked?isalpha(<mychar>) == true evaluates to false?NaN to Bool conversion: True or False?Understanding the bit shifting in C++C# bool operations to detect any false returnWhy does GCC generate 15-20% faster code if I optimize for size instead of speed?Why is f(i = -1, i = -1) undefined behavior?Why does std::foreach not work with a std::vector<bool>?Why Clang at Coliru can't compile vector::push_back?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;
If I get a bool
variable and set its second bit to 1, then variable evaluates to true and false at the same time. Compile the following code with gcc6.3 with -g
option, (gcc-v6.3.0/Linux/RHEL6.0-2016-x86_64/bin/g++ -g main.cpp -o mytest_d
) and run the executable. You get the following.
How can T be equal to true and false at the same time?
value bits
----- ----
T: 1 0001
after bit change
T: 3 0011
T is true
T is false
This can happen when you call a function in a different language (say fortran) where true and false definition is different than C++. For fortran if any bits are not 0 then the value is true, if all bits are zero then the value is false.
#include <iostream>
#include <bitset>
using namespace std;
void set_bits_to_1(void* val)
char *x = static_cast<char *>(val);
for (int i = 0; i<2; i++ )= (1UL << i);
int main(int argc,char *argv[])
bool T = 3;
cout <<" value bits " <<endl;
cout <<" ----- ---- " <<endl;
cout <<" T: "<< T <<" "<< bitset<4>(T)<<endl;
set_bits_to_1(&T);
bitset<4> bit_T = bitset<4>(T);
cout <<"after bit change"<<endl;
cout <<" T: "<< T <<" "<< bit_T<<endl;
if (T )
cout <<"T is true" <<endl;
if ( T == false)
cout <<"T is false" <<endl;
///////////////////////////////////
// Fortran function that is not compatible with C++ when compiled with ifort.
logical*1 function return_true()
implicit none
return_true = 1;
end function return_true
c++ boolean undefined-behavior evaluation abi
|
show 2 more comments
If I get a bool
variable and set its second bit to 1, then variable evaluates to true and false at the same time. Compile the following code with gcc6.3 with -g
option, (gcc-v6.3.0/Linux/RHEL6.0-2016-x86_64/bin/g++ -g main.cpp -o mytest_d
) and run the executable. You get the following.
How can T be equal to true and false at the same time?
value bits
----- ----
T: 1 0001
after bit change
T: 3 0011
T is true
T is false
This can happen when you call a function in a different language (say fortran) where true and false definition is different than C++. For fortran if any bits are not 0 then the value is true, if all bits are zero then the value is false.
#include <iostream>
#include <bitset>
using namespace std;
void set_bits_to_1(void* val)
char *x = static_cast<char *>(val);
for (int i = 0; i<2; i++ )= (1UL << i);
int main(int argc,char *argv[])
bool T = 3;
cout <<" value bits " <<endl;
cout <<" ----- ---- " <<endl;
cout <<" T: "<< T <<" "<< bitset<4>(T)<<endl;
set_bits_to_1(&T);
bitset<4> bit_T = bitset<4>(T);
cout <<"after bit change"<<endl;
cout <<" T: "<< T <<" "<< bit_T<<endl;
if (T )
cout <<"T is true" <<endl;
if ( T == false)
cout <<"T is false" <<endl;
///////////////////////////////////
// Fortran function that is not compatible with C++ when compiled with ifort.
logical*1 function return_true()
implicit none
return_true = 1;
end function return_true
c++ boolean undefined-behavior evaluation abi
65
With undefined behavior, anything is possible :)
– Jeremy Friesner
May 29 at 22:06
7
not quite a duplicate of Does the C++ standard allow for an uninitialized bool to crash a program? where my answer explains that the x86-64 System V ABI specifies thatbool
is a 0 or 1, and thus the compiler is allowed to assume this when emitting code.
– Peter Cordes
May 30 at 11:02
10
There's a tension between the mathematics of booleans and their computer science representations. Mathematically, booleans have two values, so a single bit. The problem is that in c++, bools have to be addressable, but individual bits are not addressable. The standard requires implementations to make all boolean operations result in a zero or a one. Anything else is a noncompliant implementation. The standard also requires programmers to follow this rule. In particular, intentionally setting bits so that a bool has a value this is neither zero nor one is undefined behavior.
– David Hammen
May 30 at 12:05
4
Here is minimal example. GCC up to 8.3 behaves the same as in the post. With 9.1 it's different, but still is surprising. (C++ is fun!)
– Alexander Malakhov
May 30 at 16:30
3
"Doctor, it hurts when I do this."
– Dennis Williamson
May 30 at 22:20
|
show 2 more comments
If I get a bool
variable and set its second bit to 1, then variable evaluates to true and false at the same time. Compile the following code with gcc6.3 with -g
option, (gcc-v6.3.0/Linux/RHEL6.0-2016-x86_64/bin/g++ -g main.cpp -o mytest_d
) and run the executable. You get the following.
How can T be equal to true and false at the same time?
value bits
----- ----
T: 1 0001
after bit change
T: 3 0011
T is true
T is false
This can happen when you call a function in a different language (say fortran) where true and false definition is different than C++. For fortran if any bits are not 0 then the value is true, if all bits are zero then the value is false.
#include <iostream>
#include <bitset>
using namespace std;
void set_bits_to_1(void* val)
char *x = static_cast<char *>(val);
for (int i = 0; i<2; i++ )= (1UL << i);
int main(int argc,char *argv[])
bool T = 3;
cout <<" value bits " <<endl;
cout <<" ----- ---- " <<endl;
cout <<" T: "<< T <<" "<< bitset<4>(T)<<endl;
set_bits_to_1(&T);
bitset<4> bit_T = bitset<4>(T);
cout <<"after bit change"<<endl;
cout <<" T: "<< T <<" "<< bit_T<<endl;
if (T )
cout <<"T is true" <<endl;
if ( T == false)
cout <<"T is false" <<endl;
///////////////////////////////////
// Fortran function that is not compatible with C++ when compiled with ifort.
logical*1 function return_true()
implicit none
return_true = 1;
end function return_true
c++ boolean undefined-behavior evaluation abi
If I get a bool
variable and set its second bit to 1, then variable evaluates to true and false at the same time. Compile the following code with gcc6.3 with -g
option, (gcc-v6.3.0/Linux/RHEL6.0-2016-x86_64/bin/g++ -g main.cpp -o mytest_d
) and run the executable. You get the following.
How can T be equal to true and false at the same time?
value bits
----- ----
T: 1 0001
after bit change
T: 3 0011
T is true
T is false
This can happen when you call a function in a different language (say fortran) where true and false definition is different than C++. For fortran if any bits are not 0 then the value is true, if all bits are zero then the value is false.
#include <iostream>
#include <bitset>
using namespace std;
void set_bits_to_1(void* val)
char *x = static_cast<char *>(val);
for (int i = 0; i<2; i++ )= (1UL << i);
int main(int argc,char *argv[])
bool T = 3;
cout <<" value bits " <<endl;
cout <<" ----- ---- " <<endl;
cout <<" T: "<< T <<" "<< bitset<4>(T)<<endl;
set_bits_to_1(&T);
bitset<4> bit_T = bitset<4>(T);
cout <<"after bit change"<<endl;
cout <<" T: "<< T <<" "<< bit_T<<endl;
if (T )
cout <<"T is true" <<endl;
if ( T == false)
cout <<"T is false" <<endl;
///////////////////////////////////
// Fortran function that is not compatible with C++ when compiled with ifort.
logical*1 function return_true()
implicit none
return_true = 1;
end function return_true
c++ boolean undefined-behavior evaluation abi
c++ boolean undefined-behavior evaluation abi
edited Jun 3 at 9:40
bolov
39.1k11 gold badges88 silver badges153 bronze badges
39.1k11 gold badges88 silver badges153 bronze badges
asked May 29 at 21:58
BY408BY408
2062 silver badges5 bronze badges
2062 silver badges5 bronze badges
65
With undefined behavior, anything is possible :)
– Jeremy Friesner
May 29 at 22:06
7
not quite a duplicate of Does the C++ standard allow for an uninitialized bool to crash a program? where my answer explains that the x86-64 System V ABI specifies thatbool
is a 0 or 1, and thus the compiler is allowed to assume this when emitting code.
– Peter Cordes
May 30 at 11:02
10
There's a tension between the mathematics of booleans and their computer science representations. Mathematically, booleans have two values, so a single bit. The problem is that in c++, bools have to be addressable, but individual bits are not addressable. The standard requires implementations to make all boolean operations result in a zero or a one. Anything else is a noncompliant implementation. The standard also requires programmers to follow this rule. In particular, intentionally setting bits so that a bool has a value this is neither zero nor one is undefined behavior.
– David Hammen
May 30 at 12:05
4
Here is minimal example. GCC up to 8.3 behaves the same as in the post. With 9.1 it's different, but still is surprising. (C++ is fun!)
– Alexander Malakhov
May 30 at 16:30
3
"Doctor, it hurts when I do this."
– Dennis Williamson
May 30 at 22:20
|
show 2 more comments
65
With undefined behavior, anything is possible :)
– Jeremy Friesner
May 29 at 22:06
7
not quite a duplicate of Does the C++ standard allow for an uninitialized bool to crash a program? where my answer explains that the x86-64 System V ABI specifies thatbool
is a 0 or 1, and thus the compiler is allowed to assume this when emitting code.
– Peter Cordes
May 30 at 11:02
10
There's a tension between the mathematics of booleans and their computer science representations. Mathematically, booleans have two values, so a single bit. The problem is that in c++, bools have to be addressable, but individual bits are not addressable. The standard requires implementations to make all boolean operations result in a zero or a one. Anything else is a noncompliant implementation. The standard also requires programmers to follow this rule. In particular, intentionally setting bits so that a bool has a value this is neither zero nor one is undefined behavior.
– David Hammen
May 30 at 12:05
4
Here is minimal example. GCC up to 8.3 behaves the same as in the post. With 9.1 it's different, but still is surprising. (C++ is fun!)
– Alexander Malakhov
May 30 at 16:30
3
"Doctor, it hurts when I do this."
– Dennis Williamson
May 30 at 22:20
65
65
With undefined behavior, anything is possible :)
– Jeremy Friesner
May 29 at 22:06
With undefined behavior, anything is possible :)
– Jeremy Friesner
May 29 at 22:06
7
7
not quite a duplicate of Does the C++ standard allow for an uninitialized bool to crash a program? where my answer explains that the x86-64 System V ABI specifies that
bool
is a 0 or 1, and thus the compiler is allowed to assume this when emitting code.– Peter Cordes
May 30 at 11:02
not quite a duplicate of Does the C++ standard allow for an uninitialized bool to crash a program? where my answer explains that the x86-64 System V ABI specifies that
bool
is a 0 or 1, and thus the compiler is allowed to assume this when emitting code.– Peter Cordes
May 30 at 11:02
10
10
There's a tension between the mathematics of booleans and their computer science representations. Mathematically, booleans have two values, so a single bit. The problem is that in c++, bools have to be addressable, but individual bits are not addressable. The standard requires implementations to make all boolean operations result in a zero or a one. Anything else is a noncompliant implementation. The standard also requires programmers to follow this rule. In particular, intentionally setting bits so that a bool has a value this is neither zero nor one is undefined behavior.
– David Hammen
May 30 at 12:05
There's a tension between the mathematics of booleans and their computer science representations. Mathematically, booleans have two values, so a single bit. The problem is that in c++, bools have to be addressable, but individual bits are not addressable. The standard requires implementations to make all boolean operations result in a zero or a one. Anything else is a noncompliant implementation. The standard also requires programmers to follow this rule. In particular, intentionally setting bits so that a bool has a value this is neither zero nor one is undefined behavior.
– David Hammen
May 30 at 12:05
4
4
Here is minimal example. GCC up to 8.3 behaves the same as in the post. With 9.1 it's different, but still is surprising. (C++ is fun!)
– Alexander Malakhov
May 30 at 16:30
Here is minimal example. GCC up to 8.3 behaves the same as in the post. With 9.1 it's different, but still is surprising. (C++ is fun!)
– Alexander Malakhov
May 30 at 16:30
3
3
"Doctor, it hurts when I do this."
– Dennis Williamson
May 30 at 22:20
"Doctor, it hurts when I do this."
– Dennis Williamson
May 30 at 22:20
|
show 2 more comments
2 Answers
2
active
oldest
votes
In C++ the bit representation (and even the size) of a bool
is implementation defined; generally it's implemented as a char
-sized type taking 1 or 0 as possible values.
If you set its value to anything different from the allowed ones (in this specific case by aliasing a bool
through a char
and modifying its bit representation), you are breaking the rules of the language, so anything can happen. In particular, it's explicitly specified in the standard that a "broken" bool
may behave as both true
and false
(or neither true
nor false
) at the same time:
Using a
bool
value in ways described by this International Standard as “undefined,” such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neithertrue
norfalse
(C++11, [basic.fundamental], note 47)
In this particular case, you can see how it ended up in this bizarre situation: the first if
gets compiled to
movzx eax, BYTE PTR [rbp-33]
test al, al
je .L22
which loads T
in eax
(with zero extension), and skips the print if it's all zero; the next if instead is
movzx eax, BYTE PTR [rbp-33]
xor eax, 1
test al, al
je .L23
The test if(T == false)
is transformed to if(T^1)
, which flips just the low bit. This would be ok for a valid bool
, but for your "broken" one it doesn't cut it.
Notice that this bizarre sequence is only generated at low optimization levels; at higher levels this is generally going to boil down to a zero/nonzero check, and a sequence like yours is likely to become a single test/conditional branch. You will get bizarre behavior anyway in other contexts, e.g. when summing bool
values to other integers:
int foo(bool b, int i)
return i + b;
becomes
foo(bool, int):
movzx edi, dil
lea eax, [rdi+rsi]
ret
where dil
is "trusted" to be 0/1.
If your program is all C++, then the solution is simple: don't break bool
values this way, avoid messing with their bit representation and everything will go well; in particular, even if you assign from an integer to a bool
the compiler will emit the necessary code to make sure that the resulting value is a valid bool
, so your bool T = 3
is indeed safe, and T
will end up with a true
in its guts.
If instead you need to interoperate with code written in other languages that may not share the same idea of what a bool
is, just avoid bool
for "boundary" code, and marshal it as an appropriately-sized integer. It will work in conditionals & co. just as fine.
Update about the Fortran/interoperability side of the issue
Disclaimer all I know of Fortran is what I read this morning on standard documents, and that I have some punched cards with Fortran listings that I use as bookmarks, so go easy on me.
First of all, this kind of language interoperability stuff isn't part of the language standards, but of the platform ABI. As we are talking about Linux x86-64, the relevant document is the System V x86-64 ABI.
First of all, nowhere is specified that the C _Bool
type (which is defined to be the same as C++ bool
at 3.1.2 note †) has any kind of compatibility with Fortran LOGICAL
; in particular, at 9.2.2 table 9.2 specifies that "plain" LOGICAL
is mapped to signed int
. About TYPE*N
types it says that
The “
TYPE*N
” notation specifies that variables or aggregate members of typeTYPE
shall occupyN
bytes of storage.
(ibid.)
There's no equivalent type explicitly specified for LOGICAL*1
, and it's understandable: it's not even standard; indeed if you try to compile a Fortran program containing a LOGICAL*1
in Fortran 95 compliant mode you get warnings about it, both by ifort
./example.f90(2): warning #6916: Fortran 95 does not allow this length specification. [1]
logical*1, intent(in) :: x
------------^
and by gfort
./example.f90:2:13:
logical*1, intent(in) :: x
1
Error: GNU Extension: Nonstandard type declaration LOGICAL*1 at (1)
so the waters are already muddled; so, combining the two rules above, I'd go for signed char
to be safe.
However: the ABI also specifies:
The values for type
LOGICAL
are.TRUE.
implemented as 1 and.FALSE.
implemented as 0.
So, if you have a program that stores anything besides 1 and 0 in a LOGICAL
value, you are already out of spec on the Fortran side! You say:
A fortran
logical*1
has same representation asbool
, but in fortran if bits are 00000011 it istrue
, in C++ it is undefined.
This last statement is not true, the Fortran standard is representation-agnostic, and the ABI explicitly says the contrary. Indeed you can see this in action easily by checking the output of gfort for LOGICAL
comparison:
integer function logical_compare(x, y)
logical, intent(in) :: x
logical, intent(in) :: y
if (x .eqv. y) then
logical_compare = 12
else
logical_compare = 24
end if
end function logical_compare
becomes
logical_compare_:
mov eax, DWORD PTR [rsi]
mov edx, 24
cmp DWORD PTR [rdi], eax
mov eax, 12
cmovne eax, edx
ret
You'll notice that there's a straight cmp
between the two values, without normalizing them first (unlike ifort
, that is more conservative in this regard).
Even more interesting: regardless of what the ABI says, ifort by default uses a nonstandard representation for LOGICAL
; this is explained in the -fpscomp logicals
switch documentation, which also specifies some interesting details about LOGICAL
and cross-language compatibility:
Specifies that integers with a non-zero value are treated as true, integers with a zero value are treated as false. The literal constant .TRUE. has an integer value of 1, and the literal constant .FALSE. has an integer value of 0. This representation is used by Intel Fortran releases before Version 8.0 and by Fortran PowerStation.
The default is
fpscomp nologicals
, which specifies that odd integer values (low bit one) are treated as true and even integer values (low bit zero) are treated as false.
The literal constant .TRUE. has an integer value of -1, and the literal constant .FALSE. has an integer value of 0. This representation is used by Compaq Visual Fortran. The internal representation of LOGICAL values is not specified by the Fortran standard. Programs which use integer values in LOGICAL contexts, or which pass LOGICAL values to procedures written in other languages, are non-portable and may not execute correctly. Intel recommends that you avoid coding practices that depend on the internal representation of LOGICAL values.
(emphasis added)
Now, the internal representation of a LOGICAL
normally shouldn't a problem, as, from what I gather, if you play "by the rules" and don't cross language boundaries you aren't going to notice. For a standard compliant program there's no "straight conversion" between INTEGER
and LOGICAL
; the only way I see you can shove an INTEGER
into a LOGICAL
seem to be TRANSFER
, which is intrinsically non-portable and give no real guarantees, or the non-standard INTEGER
<-> LOGICAL
conversion on assignment.
The latter one is documented by gfort to always result in nonzero -> .TRUE.
, zero -> .FALSE.
, and you can see that in all cases code is generated to make this happen (even though it's convoluted code in case of ifort with the legacy representation), so you cannot seem to shove an arbitrary integer into a LOGICAL
in this way.
logical*1 function integer_to_logical(x)
integer, intent(in) :: x
integer_to_logical = x
return
end function integer_to_logical
integer_to_logical_:
mov eax, DWORD PTR [rdi]
test eax, eax
setne al
ret
The reverse conversion for a LOGICAL*1
is a straight integer zero-extension (gfort), so, to be honoring the contract in the documentation linked above, it's clearly expecting the LOGICAL
value to be 0 or 1.
But in general, the situation for these conversions is a bit of a mess, so I'd just stay away from them.
So, long story short: avoid putting INTEGER
data into LOGICAL
values, as it is bad even in Fortran, and make sure to use the correct compiler flag to get the ABI-compliant representation for booleans, and interoperability with C/C++ should be fine. But to be extra safe, I'd just use plain char
on the C++ side.
Finally, from what I gather from the documentation, in ifort there is some builtin support for interoperability with C, including booleans; you may try to leverage it.
1
I think the standard is stupid in this case.
– BY408
May 30 at 17:41
1
This is like having an 8 lanes highway, all lanes are open, but if you use any lane other than the first you will have a guaranteed accident.
– BY408
May 30 at 17:53
9
bool
effectively is one bit, it's just not implemented that way under the hood. How many bits are used to represent is an implementation detail (part of the ABI), not one that is defined by the language. I do not understand why this is a problem in the real world, although it does make for a great Stack Overflow Q&A. I do lots of interop between C++ and code in other languages, and I've never had a problem. @BY408
– Cody Gray♦
May 30 at 17:53
1
Try using gcc with intel fortran compiler. Have a fortran function that returns a logical*1 type variable that is a 8 bit bool and assign it to a C++ bool and see what happens :)
– BY408
May 30 at 17:56
7
@BY408 IT looks like you don't know how to properly interop between fortran and C++. Don't blame your ignorance on the C++ standard. Standards exists precisely because without them any implementation could do anything they wont and interoperability would be impossible.
– Giacomo Alzetta
May 31 at 6:56
|
show 12 more comments
This is what happens when you violate your contract with both the language and the compiler.
You probably heard somewhere that "zero is false", and "non-zero is true". That holds when you stick to the language's parameters, statically converting an int
to bool
or vice versa.
It does not hold when you start messing with bit representations. In that case, you break your contract, and enter the realm of (at the very least) implementation-defined behaviour.
Simply don't do that.
It's not up to you how a bool
is stored in memory. It's up to the compiler. If you want to change a bool
's value, either assign true
/false
, or assign an integer and use the proper conversion mechanisms provided by C++.
The C++ standard used to actually give a specific call-out to how using bool
in this manner is naughty and bad and evil ("Using a bool
value in ways described by this document as 'undefined',such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true
nor false
."), though it was removed in C++20 for editorial reasons.
4
See also: stackoverflow.com/q/54120862/560648
– Lightness Races in Orbit
May 29 at 23:22
5
It's up to the compiler. It's up to "the implementation" in C++ terms. On most platforms (including x86-64 GNU/Linux), compilers all follow an ABI (x86-64 System V) which is a separate document from the compiler. It's not up to the compiler how abool
is stored in memory, that's specified by the ABI (other than privatebool
objects that nothing outside the function can ever see; then the as-if rule is in full force). "Up to the compiler" is a useful simplification, but it's not really true, especially for compilers other than GCC (because GCC devs designed the x86-64 System V ABI.)
– Peter Cordes
May 30 at 11:14
1
@PeterCordes That's correct. As you say, it's a useful simplification, and completely warranted in this context IMO.
– Lightness Races in Orbit
May 30 at 11:16
2
My answer on the uninitialized-bool UB question you linked covers all that, though :) +1 don't mess with the object-representation ofbool
.
– Peter Cordes
May 30 at 11:16
4
@BY408, if you need a type to handle values other than 1 and 0, then you shouldn't use a bool. Use an unsigned char or int or other number variable. A unsigned char used in an if() statement behaves the way you want (zero is false, nonzero is true).
– JPhi1618
May 31 at 4:30
|
show 9 more comments
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56369080%2fsetting-extra-bits-in-a-bool-makes-it-true-and-false-at-the-same-time%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
In C++ the bit representation (and even the size) of a bool
is implementation defined; generally it's implemented as a char
-sized type taking 1 or 0 as possible values.
If you set its value to anything different from the allowed ones (in this specific case by aliasing a bool
through a char
and modifying its bit representation), you are breaking the rules of the language, so anything can happen. In particular, it's explicitly specified in the standard that a "broken" bool
may behave as both true
and false
(or neither true
nor false
) at the same time:
Using a
bool
value in ways described by this International Standard as “undefined,” such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neithertrue
norfalse
(C++11, [basic.fundamental], note 47)
In this particular case, you can see how it ended up in this bizarre situation: the first if
gets compiled to
movzx eax, BYTE PTR [rbp-33]
test al, al
je .L22
which loads T
in eax
(with zero extension), and skips the print if it's all zero; the next if instead is
movzx eax, BYTE PTR [rbp-33]
xor eax, 1
test al, al
je .L23
The test if(T == false)
is transformed to if(T^1)
, which flips just the low bit. This would be ok for a valid bool
, but for your "broken" one it doesn't cut it.
Notice that this bizarre sequence is only generated at low optimization levels; at higher levels this is generally going to boil down to a zero/nonzero check, and a sequence like yours is likely to become a single test/conditional branch. You will get bizarre behavior anyway in other contexts, e.g. when summing bool
values to other integers:
int foo(bool b, int i)
return i + b;
becomes
foo(bool, int):
movzx edi, dil
lea eax, [rdi+rsi]
ret
where dil
is "trusted" to be 0/1.
If your program is all C++, then the solution is simple: don't break bool
values this way, avoid messing with their bit representation and everything will go well; in particular, even if you assign from an integer to a bool
the compiler will emit the necessary code to make sure that the resulting value is a valid bool
, so your bool T = 3
is indeed safe, and T
will end up with a true
in its guts.
If instead you need to interoperate with code written in other languages that may not share the same idea of what a bool
is, just avoid bool
for "boundary" code, and marshal it as an appropriately-sized integer. It will work in conditionals & co. just as fine.
Update about the Fortran/interoperability side of the issue
Disclaimer all I know of Fortran is what I read this morning on standard documents, and that I have some punched cards with Fortran listings that I use as bookmarks, so go easy on me.
First of all, this kind of language interoperability stuff isn't part of the language standards, but of the platform ABI. As we are talking about Linux x86-64, the relevant document is the System V x86-64 ABI.
First of all, nowhere is specified that the C _Bool
type (which is defined to be the same as C++ bool
at 3.1.2 note †) has any kind of compatibility with Fortran LOGICAL
; in particular, at 9.2.2 table 9.2 specifies that "plain" LOGICAL
is mapped to signed int
. About TYPE*N
types it says that
The “
TYPE*N
” notation specifies that variables or aggregate members of typeTYPE
shall occupyN
bytes of storage.
(ibid.)
There's no equivalent type explicitly specified for LOGICAL*1
, and it's understandable: it's not even standard; indeed if you try to compile a Fortran program containing a LOGICAL*1
in Fortran 95 compliant mode you get warnings about it, both by ifort
./example.f90(2): warning #6916: Fortran 95 does not allow this length specification. [1]
logical*1, intent(in) :: x
------------^
and by gfort
./example.f90:2:13:
logical*1, intent(in) :: x
1
Error: GNU Extension: Nonstandard type declaration LOGICAL*1 at (1)
so the waters are already muddled; so, combining the two rules above, I'd go for signed char
to be safe.
However: the ABI also specifies:
The values for type
LOGICAL
are.TRUE.
implemented as 1 and.FALSE.
implemented as 0.
So, if you have a program that stores anything besides 1 and 0 in a LOGICAL
value, you are already out of spec on the Fortran side! You say:
A fortran
logical*1
has same representation asbool
, but in fortran if bits are 00000011 it istrue
, in C++ it is undefined.
This last statement is not true, the Fortran standard is representation-agnostic, and the ABI explicitly says the contrary. Indeed you can see this in action easily by checking the output of gfort for LOGICAL
comparison:
integer function logical_compare(x, y)
logical, intent(in) :: x
logical, intent(in) :: y
if (x .eqv. y) then
logical_compare = 12
else
logical_compare = 24
end if
end function logical_compare
becomes
logical_compare_:
mov eax, DWORD PTR [rsi]
mov edx, 24
cmp DWORD PTR [rdi], eax
mov eax, 12
cmovne eax, edx
ret
You'll notice that there's a straight cmp
between the two values, without normalizing them first (unlike ifort
, that is more conservative in this regard).
Even more interesting: regardless of what the ABI says, ifort by default uses a nonstandard representation for LOGICAL
; this is explained in the -fpscomp logicals
switch documentation, which also specifies some interesting details about LOGICAL
and cross-language compatibility:
Specifies that integers with a non-zero value are treated as true, integers with a zero value are treated as false. The literal constant .TRUE. has an integer value of 1, and the literal constant .FALSE. has an integer value of 0. This representation is used by Intel Fortran releases before Version 8.0 and by Fortran PowerStation.
The default is
fpscomp nologicals
, which specifies that odd integer values (low bit one) are treated as true and even integer values (low bit zero) are treated as false.
The literal constant .TRUE. has an integer value of -1, and the literal constant .FALSE. has an integer value of 0. This representation is used by Compaq Visual Fortran. The internal representation of LOGICAL values is not specified by the Fortran standard. Programs which use integer values in LOGICAL contexts, or which pass LOGICAL values to procedures written in other languages, are non-portable and may not execute correctly. Intel recommends that you avoid coding practices that depend on the internal representation of LOGICAL values.
(emphasis added)
Now, the internal representation of a LOGICAL
normally shouldn't a problem, as, from what I gather, if you play "by the rules" and don't cross language boundaries you aren't going to notice. For a standard compliant program there's no "straight conversion" between INTEGER
and LOGICAL
; the only way I see you can shove an INTEGER
into a LOGICAL
seem to be TRANSFER
, which is intrinsically non-portable and give no real guarantees, or the non-standard INTEGER
<-> LOGICAL
conversion on assignment.
The latter one is documented by gfort to always result in nonzero -> .TRUE.
, zero -> .FALSE.
, and you can see that in all cases code is generated to make this happen (even though it's convoluted code in case of ifort with the legacy representation), so you cannot seem to shove an arbitrary integer into a LOGICAL
in this way.
logical*1 function integer_to_logical(x)
integer, intent(in) :: x
integer_to_logical = x
return
end function integer_to_logical
integer_to_logical_:
mov eax, DWORD PTR [rdi]
test eax, eax
setne al
ret
The reverse conversion for a LOGICAL*1
is a straight integer zero-extension (gfort), so, to be honoring the contract in the documentation linked above, it's clearly expecting the LOGICAL
value to be 0 or 1.
But in general, the situation for these conversions is a bit of a mess, so I'd just stay away from them.
So, long story short: avoid putting INTEGER
data into LOGICAL
values, as it is bad even in Fortran, and make sure to use the correct compiler flag to get the ABI-compliant representation for booleans, and interoperability with C/C++ should be fine. But to be extra safe, I'd just use plain char
on the C++ side.
Finally, from what I gather from the documentation, in ifort there is some builtin support for interoperability with C, including booleans; you may try to leverage it.
1
I think the standard is stupid in this case.
– BY408
May 30 at 17:41
1
This is like having an 8 lanes highway, all lanes are open, but if you use any lane other than the first you will have a guaranteed accident.
– BY408
May 30 at 17:53
9
bool
effectively is one bit, it's just not implemented that way under the hood. How many bits are used to represent is an implementation detail (part of the ABI), not one that is defined by the language. I do not understand why this is a problem in the real world, although it does make for a great Stack Overflow Q&A. I do lots of interop between C++ and code in other languages, and I've never had a problem. @BY408
– Cody Gray♦
May 30 at 17:53
1
Try using gcc with intel fortran compiler. Have a fortran function that returns a logical*1 type variable that is a 8 bit bool and assign it to a C++ bool and see what happens :)
– BY408
May 30 at 17:56
7
@BY408 IT looks like you don't know how to properly interop between fortran and C++. Don't blame your ignorance on the C++ standard. Standards exists precisely because without them any implementation could do anything they wont and interoperability would be impossible.
– Giacomo Alzetta
May 31 at 6:56
|
show 12 more comments
In C++ the bit representation (and even the size) of a bool
is implementation defined; generally it's implemented as a char
-sized type taking 1 or 0 as possible values.
If you set its value to anything different from the allowed ones (in this specific case by aliasing a bool
through a char
and modifying its bit representation), you are breaking the rules of the language, so anything can happen. In particular, it's explicitly specified in the standard that a "broken" bool
may behave as both true
and false
(or neither true
nor false
) at the same time:
Using a
bool
value in ways described by this International Standard as “undefined,” such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neithertrue
norfalse
(C++11, [basic.fundamental], note 47)
In this particular case, you can see how it ended up in this bizarre situation: the first if
gets compiled to
movzx eax, BYTE PTR [rbp-33]
test al, al
je .L22
which loads T
in eax
(with zero extension), and skips the print if it's all zero; the next if instead is
movzx eax, BYTE PTR [rbp-33]
xor eax, 1
test al, al
je .L23
The test if(T == false)
is transformed to if(T^1)
, which flips just the low bit. This would be ok for a valid bool
, but for your "broken" one it doesn't cut it.
Notice that this bizarre sequence is only generated at low optimization levels; at higher levels this is generally going to boil down to a zero/nonzero check, and a sequence like yours is likely to become a single test/conditional branch. You will get bizarre behavior anyway in other contexts, e.g. when summing bool
values to other integers:
int foo(bool b, int i)
return i + b;
becomes
foo(bool, int):
movzx edi, dil
lea eax, [rdi+rsi]
ret
where dil
is "trusted" to be 0/1.
If your program is all C++, then the solution is simple: don't break bool
values this way, avoid messing with their bit representation and everything will go well; in particular, even if you assign from an integer to a bool
the compiler will emit the necessary code to make sure that the resulting value is a valid bool
, so your bool T = 3
is indeed safe, and T
will end up with a true
in its guts.
If instead you need to interoperate with code written in other languages that may not share the same idea of what a bool
is, just avoid bool
for "boundary" code, and marshal it as an appropriately-sized integer. It will work in conditionals & co. just as fine.
Update about the Fortran/interoperability side of the issue
Disclaimer all I know of Fortran is what I read this morning on standard documents, and that I have some punched cards with Fortran listings that I use as bookmarks, so go easy on me.
First of all, this kind of language interoperability stuff isn't part of the language standards, but of the platform ABI. As we are talking about Linux x86-64, the relevant document is the System V x86-64 ABI.
First of all, nowhere is specified that the C _Bool
type (which is defined to be the same as C++ bool
at 3.1.2 note †) has any kind of compatibility with Fortran LOGICAL
; in particular, at 9.2.2 table 9.2 specifies that "plain" LOGICAL
is mapped to signed int
. About TYPE*N
types it says that
The “
TYPE*N
” notation specifies that variables or aggregate members of typeTYPE
shall occupyN
bytes of storage.
(ibid.)
There's no equivalent type explicitly specified for LOGICAL*1
, and it's understandable: it's not even standard; indeed if you try to compile a Fortran program containing a LOGICAL*1
in Fortran 95 compliant mode you get warnings about it, both by ifort
./example.f90(2): warning #6916: Fortran 95 does not allow this length specification. [1]
logical*1, intent(in) :: x
------------^
and by gfort
./example.f90:2:13:
logical*1, intent(in) :: x
1
Error: GNU Extension: Nonstandard type declaration LOGICAL*1 at (1)
so the waters are already muddled; so, combining the two rules above, I'd go for signed char
to be safe.
However: the ABI also specifies:
The values for type
LOGICAL
are.TRUE.
implemented as 1 and.FALSE.
implemented as 0.
So, if you have a program that stores anything besides 1 and 0 in a LOGICAL
value, you are already out of spec on the Fortran side! You say:
A fortran
logical*1
has same representation asbool
, but in fortran if bits are 00000011 it istrue
, in C++ it is undefined.
This last statement is not true, the Fortran standard is representation-agnostic, and the ABI explicitly says the contrary. Indeed you can see this in action easily by checking the output of gfort for LOGICAL
comparison:
integer function logical_compare(x, y)
logical, intent(in) :: x
logical, intent(in) :: y
if (x .eqv. y) then
logical_compare = 12
else
logical_compare = 24
end if
end function logical_compare
becomes
logical_compare_:
mov eax, DWORD PTR [rsi]
mov edx, 24
cmp DWORD PTR [rdi], eax
mov eax, 12
cmovne eax, edx
ret
You'll notice that there's a straight cmp
between the two values, without normalizing them first (unlike ifort
, that is more conservative in this regard).
Even more interesting: regardless of what the ABI says, ifort by default uses a nonstandard representation for LOGICAL
; this is explained in the -fpscomp logicals
switch documentation, which also specifies some interesting details about LOGICAL
and cross-language compatibility:
Specifies that integers with a non-zero value are treated as true, integers with a zero value are treated as false. The literal constant .TRUE. has an integer value of 1, and the literal constant .FALSE. has an integer value of 0. This representation is used by Intel Fortran releases before Version 8.0 and by Fortran PowerStation.
The default is
fpscomp nologicals
, which specifies that odd integer values (low bit one) are treated as true and even integer values (low bit zero) are treated as false.
The literal constant .TRUE. has an integer value of -1, and the literal constant .FALSE. has an integer value of 0. This representation is used by Compaq Visual Fortran. The internal representation of LOGICAL values is not specified by the Fortran standard. Programs which use integer values in LOGICAL contexts, or which pass LOGICAL values to procedures written in other languages, are non-portable and may not execute correctly. Intel recommends that you avoid coding practices that depend on the internal representation of LOGICAL values.
(emphasis added)
Now, the internal representation of a LOGICAL
normally shouldn't a problem, as, from what I gather, if you play "by the rules" and don't cross language boundaries you aren't going to notice. For a standard compliant program there's no "straight conversion" between INTEGER
and LOGICAL
; the only way I see you can shove an INTEGER
into a LOGICAL
seem to be TRANSFER
, which is intrinsically non-portable and give no real guarantees, or the non-standard INTEGER
<-> LOGICAL
conversion on assignment.
The latter one is documented by gfort to always result in nonzero -> .TRUE.
, zero -> .FALSE.
, and you can see that in all cases code is generated to make this happen (even though it's convoluted code in case of ifort with the legacy representation), so you cannot seem to shove an arbitrary integer into a LOGICAL
in this way.
logical*1 function integer_to_logical(x)
integer, intent(in) :: x
integer_to_logical = x
return
end function integer_to_logical
integer_to_logical_:
mov eax, DWORD PTR [rdi]
test eax, eax
setne al
ret
The reverse conversion for a LOGICAL*1
is a straight integer zero-extension (gfort), so, to be honoring the contract in the documentation linked above, it's clearly expecting the LOGICAL
value to be 0 or 1.
But in general, the situation for these conversions is a bit of a mess, so I'd just stay away from them.
So, long story short: avoid putting INTEGER
data into LOGICAL
values, as it is bad even in Fortran, and make sure to use the correct compiler flag to get the ABI-compliant representation for booleans, and interoperability with C/C++ should be fine. But to be extra safe, I'd just use plain char
on the C++ side.
Finally, from what I gather from the documentation, in ifort there is some builtin support for interoperability with C, including booleans; you may try to leverage it.
1
I think the standard is stupid in this case.
– BY408
May 30 at 17:41
1
This is like having an 8 lanes highway, all lanes are open, but if you use any lane other than the first you will have a guaranteed accident.
– BY408
May 30 at 17:53
9
bool
effectively is one bit, it's just not implemented that way under the hood. How many bits are used to represent is an implementation detail (part of the ABI), not one that is defined by the language. I do not understand why this is a problem in the real world, although it does make for a great Stack Overflow Q&A. I do lots of interop between C++ and code in other languages, and I've never had a problem. @BY408
– Cody Gray♦
May 30 at 17:53
1
Try using gcc with intel fortran compiler. Have a fortran function that returns a logical*1 type variable that is a 8 bit bool and assign it to a C++ bool and see what happens :)
– BY408
May 30 at 17:56
7
@BY408 IT looks like you don't know how to properly interop between fortran and C++. Don't blame your ignorance on the C++ standard. Standards exists precisely because without them any implementation could do anything they wont and interoperability would be impossible.
– Giacomo Alzetta
May 31 at 6:56
|
show 12 more comments
In C++ the bit representation (and even the size) of a bool
is implementation defined; generally it's implemented as a char
-sized type taking 1 or 0 as possible values.
If you set its value to anything different from the allowed ones (in this specific case by aliasing a bool
through a char
and modifying its bit representation), you are breaking the rules of the language, so anything can happen. In particular, it's explicitly specified in the standard that a "broken" bool
may behave as both true
and false
(or neither true
nor false
) at the same time:
Using a
bool
value in ways described by this International Standard as “undefined,” such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neithertrue
norfalse
(C++11, [basic.fundamental], note 47)
In this particular case, you can see how it ended up in this bizarre situation: the first if
gets compiled to
movzx eax, BYTE PTR [rbp-33]
test al, al
je .L22
which loads T
in eax
(with zero extension), and skips the print if it's all zero; the next if instead is
movzx eax, BYTE PTR [rbp-33]
xor eax, 1
test al, al
je .L23
The test if(T == false)
is transformed to if(T^1)
, which flips just the low bit. This would be ok for a valid bool
, but for your "broken" one it doesn't cut it.
Notice that this bizarre sequence is only generated at low optimization levels; at higher levels this is generally going to boil down to a zero/nonzero check, and a sequence like yours is likely to become a single test/conditional branch. You will get bizarre behavior anyway in other contexts, e.g. when summing bool
values to other integers:
int foo(bool b, int i)
return i + b;
becomes
foo(bool, int):
movzx edi, dil
lea eax, [rdi+rsi]
ret
where dil
is "trusted" to be 0/1.
If your program is all C++, then the solution is simple: don't break bool
values this way, avoid messing with their bit representation and everything will go well; in particular, even if you assign from an integer to a bool
the compiler will emit the necessary code to make sure that the resulting value is a valid bool
, so your bool T = 3
is indeed safe, and T
will end up with a true
in its guts.
If instead you need to interoperate with code written in other languages that may not share the same idea of what a bool
is, just avoid bool
for "boundary" code, and marshal it as an appropriately-sized integer. It will work in conditionals & co. just as fine.
Update about the Fortran/interoperability side of the issue
Disclaimer all I know of Fortran is what I read this morning on standard documents, and that I have some punched cards with Fortran listings that I use as bookmarks, so go easy on me.
First of all, this kind of language interoperability stuff isn't part of the language standards, but of the platform ABI. As we are talking about Linux x86-64, the relevant document is the System V x86-64 ABI.
First of all, nowhere is specified that the C _Bool
type (which is defined to be the same as C++ bool
at 3.1.2 note †) has any kind of compatibility with Fortran LOGICAL
; in particular, at 9.2.2 table 9.2 specifies that "plain" LOGICAL
is mapped to signed int
. About TYPE*N
types it says that
The “
TYPE*N
” notation specifies that variables or aggregate members of typeTYPE
shall occupyN
bytes of storage.
(ibid.)
There's no equivalent type explicitly specified for LOGICAL*1
, and it's understandable: it's not even standard; indeed if you try to compile a Fortran program containing a LOGICAL*1
in Fortran 95 compliant mode you get warnings about it, both by ifort
./example.f90(2): warning #6916: Fortran 95 does not allow this length specification. [1]
logical*1, intent(in) :: x
------------^
and by gfort
./example.f90:2:13:
logical*1, intent(in) :: x
1
Error: GNU Extension: Nonstandard type declaration LOGICAL*1 at (1)
so the waters are already muddled; so, combining the two rules above, I'd go for signed char
to be safe.
However: the ABI also specifies:
The values for type
LOGICAL
are.TRUE.
implemented as 1 and.FALSE.
implemented as 0.
So, if you have a program that stores anything besides 1 and 0 in a LOGICAL
value, you are already out of spec on the Fortran side! You say:
A fortran
logical*1
has same representation asbool
, but in fortran if bits are 00000011 it istrue
, in C++ it is undefined.
This last statement is not true, the Fortran standard is representation-agnostic, and the ABI explicitly says the contrary. Indeed you can see this in action easily by checking the output of gfort for LOGICAL
comparison:
integer function logical_compare(x, y)
logical, intent(in) :: x
logical, intent(in) :: y
if (x .eqv. y) then
logical_compare = 12
else
logical_compare = 24
end if
end function logical_compare
becomes
logical_compare_:
mov eax, DWORD PTR [rsi]
mov edx, 24
cmp DWORD PTR [rdi], eax
mov eax, 12
cmovne eax, edx
ret
You'll notice that there's a straight cmp
between the two values, without normalizing them first (unlike ifort
, that is more conservative in this regard).
Even more interesting: regardless of what the ABI says, ifort by default uses a nonstandard representation for LOGICAL
; this is explained in the -fpscomp logicals
switch documentation, which also specifies some interesting details about LOGICAL
and cross-language compatibility:
Specifies that integers with a non-zero value are treated as true, integers with a zero value are treated as false. The literal constant .TRUE. has an integer value of 1, and the literal constant .FALSE. has an integer value of 0. This representation is used by Intel Fortran releases before Version 8.0 and by Fortran PowerStation.
The default is
fpscomp nologicals
, which specifies that odd integer values (low bit one) are treated as true and even integer values (low bit zero) are treated as false.
The literal constant .TRUE. has an integer value of -1, and the literal constant .FALSE. has an integer value of 0. This representation is used by Compaq Visual Fortran. The internal representation of LOGICAL values is not specified by the Fortran standard. Programs which use integer values in LOGICAL contexts, or which pass LOGICAL values to procedures written in other languages, are non-portable and may not execute correctly. Intel recommends that you avoid coding practices that depend on the internal representation of LOGICAL values.
(emphasis added)
Now, the internal representation of a LOGICAL
normally shouldn't a problem, as, from what I gather, if you play "by the rules" and don't cross language boundaries you aren't going to notice. For a standard compliant program there's no "straight conversion" between INTEGER
and LOGICAL
; the only way I see you can shove an INTEGER
into a LOGICAL
seem to be TRANSFER
, which is intrinsically non-portable and give no real guarantees, or the non-standard INTEGER
<-> LOGICAL
conversion on assignment.
The latter one is documented by gfort to always result in nonzero -> .TRUE.
, zero -> .FALSE.
, and you can see that in all cases code is generated to make this happen (even though it's convoluted code in case of ifort with the legacy representation), so you cannot seem to shove an arbitrary integer into a LOGICAL
in this way.
logical*1 function integer_to_logical(x)
integer, intent(in) :: x
integer_to_logical = x
return
end function integer_to_logical
integer_to_logical_:
mov eax, DWORD PTR [rdi]
test eax, eax
setne al
ret
The reverse conversion for a LOGICAL*1
is a straight integer zero-extension (gfort), so, to be honoring the contract in the documentation linked above, it's clearly expecting the LOGICAL
value to be 0 or 1.
But in general, the situation for these conversions is a bit of a mess, so I'd just stay away from them.
So, long story short: avoid putting INTEGER
data into LOGICAL
values, as it is bad even in Fortran, and make sure to use the correct compiler flag to get the ABI-compliant representation for booleans, and interoperability with C/C++ should be fine. But to be extra safe, I'd just use plain char
on the C++ side.
Finally, from what I gather from the documentation, in ifort there is some builtin support for interoperability with C, including booleans; you may try to leverage it.
In C++ the bit representation (and even the size) of a bool
is implementation defined; generally it's implemented as a char
-sized type taking 1 or 0 as possible values.
If you set its value to anything different from the allowed ones (in this specific case by aliasing a bool
through a char
and modifying its bit representation), you are breaking the rules of the language, so anything can happen. In particular, it's explicitly specified in the standard that a "broken" bool
may behave as both true
and false
(or neither true
nor false
) at the same time:
Using a
bool
value in ways described by this International Standard as “undefined,” such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neithertrue
norfalse
(C++11, [basic.fundamental], note 47)
In this particular case, you can see how it ended up in this bizarre situation: the first if
gets compiled to
movzx eax, BYTE PTR [rbp-33]
test al, al
je .L22
which loads T
in eax
(with zero extension), and skips the print if it's all zero; the next if instead is
movzx eax, BYTE PTR [rbp-33]
xor eax, 1
test al, al
je .L23
The test if(T == false)
is transformed to if(T^1)
, which flips just the low bit. This would be ok for a valid bool
, but for your "broken" one it doesn't cut it.
Notice that this bizarre sequence is only generated at low optimization levels; at higher levels this is generally going to boil down to a zero/nonzero check, and a sequence like yours is likely to become a single test/conditional branch. You will get bizarre behavior anyway in other contexts, e.g. when summing bool
values to other integers:
int foo(bool b, int i)
return i + b;
becomes
foo(bool, int):
movzx edi, dil
lea eax, [rdi+rsi]
ret
where dil
is "trusted" to be 0/1.
If your program is all C++, then the solution is simple: don't break bool
values this way, avoid messing with their bit representation and everything will go well; in particular, even if you assign from an integer to a bool
the compiler will emit the necessary code to make sure that the resulting value is a valid bool
, so your bool T = 3
is indeed safe, and T
will end up with a true
in its guts.
If instead you need to interoperate with code written in other languages that may not share the same idea of what a bool
is, just avoid bool
for "boundary" code, and marshal it as an appropriately-sized integer. It will work in conditionals & co. just as fine.
Update about the Fortran/interoperability side of the issue
Disclaimer all I know of Fortran is what I read this morning on standard documents, and that I have some punched cards with Fortran listings that I use as bookmarks, so go easy on me.
First of all, this kind of language interoperability stuff isn't part of the language standards, but of the platform ABI. As we are talking about Linux x86-64, the relevant document is the System V x86-64 ABI.
First of all, nowhere is specified that the C _Bool
type (which is defined to be the same as C++ bool
at 3.1.2 note †) has any kind of compatibility with Fortran LOGICAL
; in particular, at 9.2.2 table 9.2 specifies that "plain" LOGICAL
is mapped to signed int
. About TYPE*N
types it says that
The “
TYPE*N
” notation specifies that variables or aggregate members of typeTYPE
shall occupyN
bytes of storage.
(ibid.)
There's no equivalent type explicitly specified for LOGICAL*1
, and it's understandable: it's not even standard; indeed if you try to compile a Fortran program containing a LOGICAL*1
in Fortran 95 compliant mode you get warnings about it, both by ifort
./example.f90(2): warning #6916: Fortran 95 does not allow this length specification. [1]
logical*1, intent(in) :: x
------------^
and by gfort
./example.f90:2:13:
logical*1, intent(in) :: x
1
Error: GNU Extension: Nonstandard type declaration LOGICAL*1 at (1)
so the waters are already muddled; so, combining the two rules above, I'd go for signed char
to be safe.
However: the ABI also specifies:
The values for type
LOGICAL
are.TRUE.
implemented as 1 and.FALSE.
implemented as 0.
So, if you have a program that stores anything besides 1 and 0 in a LOGICAL
value, you are already out of spec on the Fortran side! You say:
A fortran
logical*1
has same representation asbool
, but in fortran if bits are 00000011 it istrue
, in C++ it is undefined.
This last statement is not true, the Fortran standard is representation-agnostic, and the ABI explicitly says the contrary. Indeed you can see this in action easily by checking the output of gfort for LOGICAL
comparison:
integer function logical_compare(x, y)
logical, intent(in) :: x
logical, intent(in) :: y
if (x .eqv. y) then
logical_compare = 12
else
logical_compare = 24
end if
end function logical_compare
becomes
logical_compare_:
mov eax, DWORD PTR [rsi]
mov edx, 24
cmp DWORD PTR [rdi], eax
mov eax, 12
cmovne eax, edx
ret
You'll notice that there's a straight cmp
between the two values, without normalizing them first (unlike ifort
, that is more conservative in this regard).
Even more interesting: regardless of what the ABI says, ifort by default uses a nonstandard representation for LOGICAL
; this is explained in the -fpscomp logicals
switch documentation, which also specifies some interesting details about LOGICAL
and cross-language compatibility:
Specifies that integers with a non-zero value are treated as true, integers with a zero value are treated as false. The literal constant .TRUE. has an integer value of 1, and the literal constant .FALSE. has an integer value of 0. This representation is used by Intel Fortran releases before Version 8.0 and by Fortran PowerStation.
The default is
fpscomp nologicals
, which specifies that odd integer values (low bit one) are treated as true and even integer values (low bit zero) are treated as false.
The literal constant .TRUE. has an integer value of -1, and the literal constant .FALSE. has an integer value of 0. This representation is used by Compaq Visual Fortran. The internal representation of LOGICAL values is not specified by the Fortran standard. Programs which use integer values in LOGICAL contexts, or which pass LOGICAL values to procedures written in other languages, are non-portable and may not execute correctly. Intel recommends that you avoid coding practices that depend on the internal representation of LOGICAL values.
(emphasis added)
Now, the internal representation of a LOGICAL
normally shouldn't a problem, as, from what I gather, if you play "by the rules" and don't cross language boundaries you aren't going to notice. For a standard compliant program there's no "straight conversion" between INTEGER
and LOGICAL
; the only way I see you can shove an INTEGER
into a LOGICAL
seem to be TRANSFER
, which is intrinsically non-portable and give no real guarantees, or the non-standard INTEGER
<-> LOGICAL
conversion on assignment.
The latter one is documented by gfort to always result in nonzero -> .TRUE.
, zero -> .FALSE.
, and you can see that in all cases code is generated to make this happen (even though it's convoluted code in case of ifort with the legacy representation), so you cannot seem to shove an arbitrary integer into a LOGICAL
in this way.
logical*1 function integer_to_logical(x)
integer, intent(in) :: x
integer_to_logical = x
return
end function integer_to_logical
integer_to_logical_:
mov eax, DWORD PTR [rdi]
test eax, eax
setne al
ret
The reverse conversion for a LOGICAL*1
is a straight integer zero-extension (gfort), so, to be honoring the contract in the documentation linked above, it's clearly expecting the LOGICAL
value to be 0 or 1.
But in general, the situation for these conversions is a bit of a mess, so I'd just stay away from them.
So, long story short: avoid putting INTEGER
data into LOGICAL
values, as it is bad even in Fortran, and make sure to use the correct compiler flag to get the ABI-compliant representation for booleans, and interoperability with C/C++ should be fine. But to be extra safe, I'd just use plain char
on the C++ side.
Finally, from what I gather from the documentation, in ifort there is some builtin support for interoperability with C, including booleans; you may try to leverage it.
edited May 31 at 13:06
answered May 29 at 22:32
Matteo ItaliaMatteo Italia
106k15 gold badges162 silver badges258 bronze badges
106k15 gold badges162 silver badges258 bronze badges
1
I think the standard is stupid in this case.
– BY408
May 30 at 17:41
1
This is like having an 8 lanes highway, all lanes are open, but if you use any lane other than the first you will have a guaranteed accident.
– BY408
May 30 at 17:53
9
bool
effectively is one bit, it's just not implemented that way under the hood. How many bits are used to represent is an implementation detail (part of the ABI), not one that is defined by the language. I do not understand why this is a problem in the real world, although it does make for a great Stack Overflow Q&A. I do lots of interop between C++ and code in other languages, and I've never had a problem. @BY408
– Cody Gray♦
May 30 at 17:53
1
Try using gcc with intel fortran compiler. Have a fortran function that returns a logical*1 type variable that is a 8 bit bool and assign it to a C++ bool and see what happens :)
– BY408
May 30 at 17:56
7
@BY408 IT looks like you don't know how to properly interop between fortran and C++. Don't blame your ignorance on the C++ standard. Standards exists precisely because without them any implementation could do anything they wont and interoperability would be impossible.
– Giacomo Alzetta
May 31 at 6:56
|
show 12 more comments
1
I think the standard is stupid in this case.
– BY408
May 30 at 17:41
1
This is like having an 8 lanes highway, all lanes are open, but if you use any lane other than the first you will have a guaranteed accident.
– BY408
May 30 at 17:53
9
bool
effectively is one bit, it's just not implemented that way under the hood. How many bits are used to represent is an implementation detail (part of the ABI), not one that is defined by the language. I do not understand why this is a problem in the real world, although it does make for a great Stack Overflow Q&A. I do lots of interop between C++ and code in other languages, and I've never had a problem. @BY408
– Cody Gray♦
May 30 at 17:53
1
Try using gcc with intel fortran compiler. Have a fortran function that returns a logical*1 type variable that is a 8 bit bool and assign it to a C++ bool and see what happens :)
– BY408
May 30 at 17:56
7
@BY408 IT looks like you don't know how to properly interop between fortran and C++. Don't blame your ignorance on the C++ standard. Standards exists precisely because without them any implementation could do anything they wont and interoperability would be impossible.
– Giacomo Alzetta
May 31 at 6:56
1
1
I think the standard is stupid in this case.
– BY408
May 30 at 17:41
I think the standard is stupid in this case.
– BY408
May 30 at 17:41
1
1
This is like having an 8 lanes highway, all lanes are open, but if you use any lane other than the first you will have a guaranteed accident.
– BY408
May 30 at 17:53
This is like having an 8 lanes highway, all lanes are open, but if you use any lane other than the first you will have a guaranteed accident.
– BY408
May 30 at 17:53
9
9
bool
effectively is one bit, it's just not implemented that way under the hood. How many bits are used to represent is an implementation detail (part of the ABI), not one that is defined by the language. I do not understand why this is a problem in the real world, although it does make for a great Stack Overflow Q&A. I do lots of interop between C++ and code in other languages, and I've never had a problem. @BY408– Cody Gray♦
May 30 at 17:53
bool
effectively is one bit, it's just not implemented that way under the hood. How many bits are used to represent is an implementation detail (part of the ABI), not one that is defined by the language. I do not understand why this is a problem in the real world, although it does make for a great Stack Overflow Q&A. I do lots of interop between C++ and code in other languages, and I've never had a problem. @BY408– Cody Gray♦
May 30 at 17:53
1
1
Try using gcc with intel fortran compiler. Have a fortran function that returns a logical*1 type variable that is a 8 bit bool and assign it to a C++ bool and see what happens :)
– BY408
May 30 at 17:56
Try using gcc with intel fortran compiler. Have a fortran function that returns a logical*1 type variable that is a 8 bit bool and assign it to a C++ bool and see what happens :)
– BY408
May 30 at 17:56
7
7
@BY408 IT looks like you don't know how to properly interop between fortran and C++. Don't blame your ignorance on the C++ standard. Standards exists precisely because without them any implementation could do anything they wont and interoperability would be impossible.
– Giacomo Alzetta
May 31 at 6:56
@BY408 IT looks like you don't know how to properly interop between fortran and C++. Don't blame your ignorance on the C++ standard. Standards exists precisely because without them any implementation could do anything they wont and interoperability would be impossible.
– Giacomo Alzetta
May 31 at 6:56
|
show 12 more comments
This is what happens when you violate your contract with both the language and the compiler.
You probably heard somewhere that "zero is false", and "non-zero is true". That holds when you stick to the language's parameters, statically converting an int
to bool
or vice versa.
It does not hold when you start messing with bit representations. In that case, you break your contract, and enter the realm of (at the very least) implementation-defined behaviour.
Simply don't do that.
It's not up to you how a bool
is stored in memory. It's up to the compiler. If you want to change a bool
's value, either assign true
/false
, or assign an integer and use the proper conversion mechanisms provided by C++.
The C++ standard used to actually give a specific call-out to how using bool
in this manner is naughty and bad and evil ("Using a bool
value in ways described by this document as 'undefined',such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true
nor false
."), though it was removed in C++20 for editorial reasons.
4
See also: stackoverflow.com/q/54120862/560648
– Lightness Races in Orbit
May 29 at 23:22
5
It's up to the compiler. It's up to "the implementation" in C++ terms. On most platforms (including x86-64 GNU/Linux), compilers all follow an ABI (x86-64 System V) which is a separate document from the compiler. It's not up to the compiler how abool
is stored in memory, that's specified by the ABI (other than privatebool
objects that nothing outside the function can ever see; then the as-if rule is in full force). "Up to the compiler" is a useful simplification, but it's not really true, especially for compilers other than GCC (because GCC devs designed the x86-64 System V ABI.)
– Peter Cordes
May 30 at 11:14
1
@PeterCordes That's correct. As you say, it's a useful simplification, and completely warranted in this context IMO.
– Lightness Races in Orbit
May 30 at 11:16
2
My answer on the uninitialized-bool UB question you linked covers all that, though :) +1 don't mess with the object-representation ofbool
.
– Peter Cordes
May 30 at 11:16
4
@BY408, if you need a type to handle values other than 1 and 0, then you shouldn't use a bool. Use an unsigned char or int or other number variable. A unsigned char used in an if() statement behaves the way you want (zero is false, nonzero is true).
– JPhi1618
May 31 at 4:30
|
show 9 more comments
This is what happens when you violate your contract with both the language and the compiler.
You probably heard somewhere that "zero is false", and "non-zero is true". That holds when you stick to the language's parameters, statically converting an int
to bool
or vice versa.
It does not hold when you start messing with bit representations. In that case, you break your contract, and enter the realm of (at the very least) implementation-defined behaviour.
Simply don't do that.
It's not up to you how a bool
is stored in memory. It's up to the compiler. If you want to change a bool
's value, either assign true
/false
, or assign an integer and use the proper conversion mechanisms provided by C++.
The C++ standard used to actually give a specific call-out to how using bool
in this manner is naughty and bad and evil ("Using a bool
value in ways described by this document as 'undefined',such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true
nor false
."), though it was removed in C++20 for editorial reasons.
4
See also: stackoverflow.com/q/54120862/560648
– Lightness Races in Orbit
May 29 at 23:22
5
It's up to the compiler. It's up to "the implementation" in C++ terms. On most platforms (including x86-64 GNU/Linux), compilers all follow an ABI (x86-64 System V) which is a separate document from the compiler. It's not up to the compiler how abool
is stored in memory, that's specified by the ABI (other than privatebool
objects that nothing outside the function can ever see; then the as-if rule is in full force). "Up to the compiler" is a useful simplification, but it's not really true, especially for compilers other than GCC (because GCC devs designed the x86-64 System V ABI.)
– Peter Cordes
May 30 at 11:14
1
@PeterCordes That's correct. As you say, it's a useful simplification, and completely warranted in this context IMO.
– Lightness Races in Orbit
May 30 at 11:16
2
My answer on the uninitialized-bool UB question you linked covers all that, though :) +1 don't mess with the object-representation ofbool
.
– Peter Cordes
May 30 at 11:16
4
@BY408, if you need a type to handle values other than 1 and 0, then you shouldn't use a bool. Use an unsigned char or int or other number variable. A unsigned char used in an if() statement behaves the way you want (zero is false, nonzero is true).
– JPhi1618
May 31 at 4:30
|
show 9 more comments
This is what happens when you violate your contract with both the language and the compiler.
You probably heard somewhere that "zero is false", and "non-zero is true". That holds when you stick to the language's parameters, statically converting an int
to bool
or vice versa.
It does not hold when you start messing with bit representations. In that case, you break your contract, and enter the realm of (at the very least) implementation-defined behaviour.
Simply don't do that.
It's not up to you how a bool
is stored in memory. It's up to the compiler. If you want to change a bool
's value, either assign true
/false
, or assign an integer and use the proper conversion mechanisms provided by C++.
The C++ standard used to actually give a specific call-out to how using bool
in this manner is naughty and bad and evil ("Using a bool
value in ways described by this document as 'undefined',such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true
nor false
."), though it was removed in C++20 for editorial reasons.
This is what happens when you violate your contract with both the language and the compiler.
You probably heard somewhere that "zero is false", and "non-zero is true". That holds when you stick to the language's parameters, statically converting an int
to bool
or vice versa.
It does not hold when you start messing with bit representations. In that case, you break your contract, and enter the realm of (at the very least) implementation-defined behaviour.
Simply don't do that.
It's not up to you how a bool
is stored in memory. It's up to the compiler. If you want to change a bool
's value, either assign true
/false
, or assign an integer and use the proper conversion mechanisms provided by C++.
The C++ standard used to actually give a specific call-out to how using bool
in this manner is naughty and bad and evil ("Using a bool
value in ways described by this document as 'undefined',such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true
nor false
."), though it was removed in C++20 for editorial reasons.
edited Jun 10 at 14:43
answered May 29 at 23:17
Lightness Races in OrbitLightness Races in Orbit
318k59 gold badges527 silver badges880 bronze badges
318k59 gold badges527 silver badges880 bronze badges
4
See also: stackoverflow.com/q/54120862/560648
– Lightness Races in Orbit
May 29 at 23:22
5
It's up to the compiler. It's up to "the implementation" in C++ terms. On most platforms (including x86-64 GNU/Linux), compilers all follow an ABI (x86-64 System V) which is a separate document from the compiler. It's not up to the compiler how abool
is stored in memory, that's specified by the ABI (other than privatebool
objects that nothing outside the function can ever see; then the as-if rule is in full force). "Up to the compiler" is a useful simplification, but it's not really true, especially for compilers other than GCC (because GCC devs designed the x86-64 System V ABI.)
– Peter Cordes
May 30 at 11:14
1
@PeterCordes That's correct. As you say, it's a useful simplification, and completely warranted in this context IMO.
– Lightness Races in Orbit
May 30 at 11:16
2
My answer on the uninitialized-bool UB question you linked covers all that, though :) +1 don't mess with the object-representation ofbool
.
– Peter Cordes
May 30 at 11:16
4
@BY408, if you need a type to handle values other than 1 and 0, then you shouldn't use a bool. Use an unsigned char or int or other number variable. A unsigned char used in an if() statement behaves the way you want (zero is false, nonzero is true).
– JPhi1618
May 31 at 4:30
|
show 9 more comments
4
See also: stackoverflow.com/q/54120862/560648
– Lightness Races in Orbit
May 29 at 23:22
5
It's up to the compiler. It's up to "the implementation" in C++ terms. On most platforms (including x86-64 GNU/Linux), compilers all follow an ABI (x86-64 System V) which is a separate document from the compiler. It's not up to the compiler how abool
is stored in memory, that's specified by the ABI (other than privatebool
objects that nothing outside the function can ever see; then the as-if rule is in full force). "Up to the compiler" is a useful simplification, but it's not really true, especially for compilers other than GCC (because GCC devs designed the x86-64 System V ABI.)
– Peter Cordes
May 30 at 11:14
1
@PeterCordes That's correct. As you say, it's a useful simplification, and completely warranted in this context IMO.
– Lightness Races in Orbit
May 30 at 11:16
2
My answer on the uninitialized-bool UB question you linked covers all that, though :) +1 don't mess with the object-representation ofbool
.
– Peter Cordes
May 30 at 11:16
4
@BY408, if you need a type to handle values other than 1 and 0, then you shouldn't use a bool. Use an unsigned char or int or other number variable. A unsigned char used in an if() statement behaves the way you want (zero is false, nonzero is true).
– JPhi1618
May 31 at 4:30
4
4
See also: stackoverflow.com/q/54120862/560648
– Lightness Races in Orbit
May 29 at 23:22
See also: stackoverflow.com/q/54120862/560648
– Lightness Races in Orbit
May 29 at 23:22
5
5
It's up to the compiler. It's up to "the implementation" in C++ terms. On most platforms (including x86-64 GNU/Linux), compilers all follow an ABI (x86-64 System V) which is a separate document from the compiler. It's not up to the compiler how a
bool
is stored in memory, that's specified by the ABI (other than private bool
objects that nothing outside the function can ever see; then the as-if rule is in full force). "Up to the compiler" is a useful simplification, but it's not really true, especially for compilers other than GCC (because GCC devs designed the x86-64 System V ABI.)– Peter Cordes
May 30 at 11:14
It's up to the compiler. It's up to "the implementation" in C++ terms. On most platforms (including x86-64 GNU/Linux), compilers all follow an ABI (x86-64 System V) which is a separate document from the compiler. It's not up to the compiler how a
bool
is stored in memory, that's specified by the ABI (other than private bool
objects that nothing outside the function can ever see; then the as-if rule is in full force). "Up to the compiler" is a useful simplification, but it's not really true, especially for compilers other than GCC (because GCC devs designed the x86-64 System V ABI.)– Peter Cordes
May 30 at 11:14
1
1
@PeterCordes That's correct. As you say, it's a useful simplification, and completely warranted in this context IMO.
– Lightness Races in Orbit
May 30 at 11:16
@PeterCordes That's correct. As you say, it's a useful simplification, and completely warranted in this context IMO.
– Lightness Races in Orbit
May 30 at 11:16
2
2
My answer on the uninitialized-bool UB question you linked covers all that, though :) +1 don't mess with the object-representation of
bool
.– Peter Cordes
May 30 at 11:16
My answer on the uninitialized-bool UB question you linked covers all that, though :) +1 don't mess with the object-representation of
bool
.– Peter Cordes
May 30 at 11:16
4
4
@BY408, if you need a type to handle values other than 1 and 0, then you shouldn't use a bool. Use an unsigned char or int or other number variable. A unsigned char used in an if() statement behaves the way you want (zero is false, nonzero is true).
– JPhi1618
May 31 at 4:30
@BY408, if you need a type to handle values other than 1 and 0, then you shouldn't use a bool. Use an unsigned char or int or other number variable. A unsigned char used in an if() statement behaves the way you want (zero is false, nonzero is true).
– JPhi1618
May 31 at 4:30
|
show 9 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56369080%2fsetting-extra-bits-in-a-bool-makes-it-true-and-false-at-the-same-time%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
65
With undefined behavior, anything is possible :)
– Jeremy Friesner
May 29 at 22:06
7
not quite a duplicate of Does the C++ standard allow for an uninitialized bool to crash a program? where my answer explains that the x86-64 System V ABI specifies that
bool
is a 0 or 1, and thus the compiler is allowed to assume this when emitting code.– Peter Cordes
May 30 at 11:02
10
There's a tension between the mathematics of booleans and their computer science representations. Mathematically, booleans have two values, so a single bit. The problem is that in c++, bools have to be addressable, but individual bits are not addressable. The standard requires implementations to make all boolean operations result in a zero or a one. Anything else is a noncompliant implementation. The standard also requires programmers to follow this rule. In particular, intentionally setting bits so that a bool has a value this is neither zero nor one is undefined behavior.
– David Hammen
May 30 at 12:05
4
Here is minimal example. GCC up to 8.3 behaves the same as in the post. With 9.1 it's different, but still is surprising. (C++ is fun!)
– Alexander Malakhov
May 30 at 16:30
3
"Doctor, it hurts when I do this."
– Dennis Williamson
May 30 at 22:20