xnor
xnor

Reputation: 353

Why doesn't comparison with bool convert to bool in C11?

Given the following C11 code:

int a = 1234;

bool b = (bool)a; // equivalent to (a != 0) or (a != false) which evaluates to 1 or true

if (a) // equivalent to if(a != 0) or if (a != false) which evaluates to 1 or true
  printf("a\n");

if (a == (bool)true) // should be equivalent to if(!(a == false)) or if (a != false)
  printf("a == (bool)true\n");

I understand that true is #define true 1 but a bool clearly is not an ordinary integral type because something like (bool)0.1 evaluates to 1 while a cast to int would result in 0.

1) Why wasn't true defined as (bool)1? This would allow the compiler to at least output a warning.

2) Why is the integer in my example not converted into a bool such that a == (bool)true would evaluate to (bool)a == (bool)true which actually would be true?

Upvotes: 0

Views: 3871

Answers (3)

chux
chux

Reputation: 153498

Why doesn't comparison with bool convert to bool in C11?

_Bool is the lowest rank and equality operator == specifies that its _Bool operands are promoted to int. @StoryTeller


The rank of _Bool shall be less than the rank of all other standard integer types. C11 §6.3.1.1 1

(Equality operators) If both of the operands have arithmetic type, the usual arithmetic conversions are performed. §6.5.9 4

(usual arithmetic conversions) ... the integer promotions are performed on both operands §6.3.1.8 1

(integer promotions) If an int can represent all values of the original type ... the value is converted to an int ... §6.3.1.1 2


OP's code samples did not have a "comparison with bool".

// int compared to int: false since a == 1234 and that is not equal 1
if (a == true)  

Instead could have had

// int compared to _Bool: false since a == 1234 and that is not equal to 0 or 1
if (a == b) 

With int == _Bool, int == short, int == signed char, the same thing occurs. The lower rank operand is promoted to int.


1) Why wasn't true defined as (bool)1? This would allow the compiler to at least output a warning.

Why? a standard committee decision of years ago. Considering true as (int)1 rather than (_Bool)1 certainly would have impacted the existing code less when _Bool was introduced. (C99). This is consistent with other sub-int constants like SHRT_MAX which is usually an int, not short. In any case, in most contexts, a promotion to int/unsigned would occur anyway before further processing - like in this compare case.

Further (_Bool)1 is not needed to allow a compiler to provide a warning. A compiler can be made that supplies a warning using various analytic tools. As (_Bool)1, it would simplify things for a compiler to provide such a warning though.

2) Why is the integer in my example not converted into a bool such that a == true would evaluate to (bool)a == true which actually would be true?

As true is an (int)1, with a == true, both operands are int. _Bool does not apply here.

2) [OP Updated] Why is the integer in my example not converted into a bool such that a == true would evaluate to (bool)a == true which actually would be true?

The top of the answer addresses this: true in an int, so (bool)a is promoted to an int before the comparison as int is higher rank than _Bool.

Upvotes: 4

xnor
xnor

Reputation: 353

Summarizing from the comments:

_Bool is actually "just" another unsigned integer type with the exceptional rule that conversions to it always result in 1 ("true") if the value does not equal 0 ("false").

Because it is an unsigned integer, integral promotion rules apply. Even if true was defined as (bool)1 the comparison a == true results in actually comparing 1234 == 1 which is always false.

Upvotes: -2

Keith Thompson
Keith Thompson

Reputation: 263267

I understand that true is #define true 1 but a bool clearly is not an ordinary integral type because something like (bool)0.1 evaluates to 1 while a cast to int would result in 0.

bool (actually _Bool; bool is a macro that expands to _Bool) is an integer type. It does have a somewhat unusual feature that it doesn't share with any other type: converting any non-zero value to _Bool yields 1.

Note that the operators that yield logically Boolean values still yield an int result with the value 0 or 1. This generally isn't a problem due to implicit conversions.

1) Why wasn't true defined as (bool)1? This would allow the compiler to at least output a warning.

Compilers are allowed to produce warnings whenever they like. A warning on a comparison to true or false would be a good idea.

2) Why is the integer in my example not converted into a bool such that a == true would evaluate to (bool)a == true which actually would be true?

a == true has to convert its operands to the same type before it can compare them. This is done via the usual arithmetic conversions. The rules (which are fairly complex) are described in the N1570 section 6.3.1.8. A very quick and imprecise summary is that the operand of the narrower type (lesser integer conversion rank) is converted to the type of the other operand. For example, when you compare expressions of types int and long, the int operand is promoted to long. _Bool is the narrowest integer type, so it's always promoted in a comparison unless it's being compared another _Bool value.

Changing these rules just for _Bool would be confusing, and it really wouldn't buy you much.

Bottom line: Don't compare values for equality to false or true. Don't write:

if (a == true)

just write:

if (a)

if that's what you mean. Similarly, don't write:

if (a == false)

just write

if (!a)

Upvotes: 2

Related Questions