"a < b < c" not the same as "(a < b) && (b < c)"?

T

Tim Rentsch

Dik T. Winter said:
But that is not really relevant. The operators will not return the boolean
values for a long time. And once they return it: a < b < c becomes
illegal I think.

Making 'a < b < c' illegal would require changes beyond having the
relational/equality operators return _Bool's, since _Bool is still
an (unsigned) integer type, 0 for false, 1 for not false, standard
integer promotions, etc. The main reason for using _Bool over
some other integer type would seem to be the different semantics
for assignment/conversion. Changing the logical operators to
return _Bool's would not, by itself, change the language in any
really significant way. (The expression 'sizeof(a<b)' might have
a different value, as one example of a difference.)
 
S

Skarmander

Keith said:
Actually, I think C89 was adopted much more quickly than C99, because
it was such an improvement over K&R C. Function prototypes in
particular caught on very well; it was also popular, I think, just
because of the need for a standard. C99 doesn't have the same
advantages.
It does have a feel of "has it been ten years already? We'd better make
a new standard".
In a quick test, I found only one C compiler that rejected the
declaration
_Bool b;
and that was an older version of a compiler whose current version does
support it. (I don't for a moment claim that the compilers I tried
were a representative sample.) And gcc, probably the most popular C
compiler out there, supports *most* of C99, including _Bool. If gcc
ever supports all of C99, I suspect that will provide the needed
pressure on the other vendors.
Possibly. It remains to be seen how popular C99 will be among
programmers. If it ever does become ubiquitous it will be natural to use
what it has to offer, of course, but I don't see many people clamoring
for the fantastic new features it has to offer. Most fall in the
category "nice, but not essentially new", with the exception of
variable-length arrays -- but those seem to be underappreciated by the
community (and are tellingly one of the things that don't yet fully work
in gcc because they don't mesh with an existing extension).

S.
 
A

August Karlstrom

Keith said:
Yes, it does. It's called _Bool (or bool with "#include <stdbool.h>").

OK, you're right, sorry. The existence of the header file has made me
think it's not a native type. So _Bool is just an integer type that can
hold two values: 0 or 1, and it's called _Bool just to minimize the risk
of breaking existing programs, right? A more extreme defensive approach
would be to call it __BoOl or something ;-)
C's (more precisely, C99's) native boolean type happens to be an
integer type, and integer type can be freely and implicitly converted
to each other.

....and that's why expressions such as `a < b < c' has the strange
(unexpected) semantics.


August
 
K

Keyser Soze

Paminu said:
In math this expression:

(a < b) && (b < c)

would be described as:

a < b < c

But why is it that in C these two expressions evaluate to something
different for the same values of a, b and c?

e.g:

for a = 0, b = 3 and c = 2:

a < b < c = 1
(a < b) && (b < c) = 0

when typed in C.

((a < b ? b : a) < c)
 
A

August Karlstrom

Keith said:
And gcc, probably the most popular C
compiler out there, supports *most* of C99, including _Bool.

It's strange that gcc (3.3.5) accepts _Bool declarations even when
compiled with the option `-std=c89':

$ cat test.c
#include <stdio.h>

int main(void)
{
_Bool b = 1;

if (b) puts("b holds true.");
return 0;
}
$ gcc -ansi -pedantic -std=c89 -Wall -o test test.c
$


August
 
D

Dik T. Winter

> Because that's the way the syntax is defined. Not very useful. Just for
> fun, try defining a grammar that doesn't allow a < b < c (that's easy),
> then try defining a grammar that allows expressions like a <= b < c == d
> <= e but not a <= b > c, then try defining the semantics like in
> mathematics. It's fun.

Why should "a <= b > c" be disallowed? It is clear what it means. And
indeed, in Ada it is easy to allow all those expressions. The only
problem (I see) is "a < (b < c)", but indeed, I have no idea what it
means.
 
K

Keith Thompson

August Karlstrom said:
OK, you're right, sorry. The existence of the header file has made me
think it's not a native type. So _Bool is just an integer type that
can hold two values: 0 or 1, and it's called _Bool just to minimize
the risk of breaking existing programs, right?

Right. It can almost certainly hold values other than 0 or 1, but
since conversion from any integer type to _Bool always yields a result
of 0 or 1, it's difficult to store any other value in a _Bool object.
A more extreme
defensive approach would be to call it __BoOl or something ;-)

That's not necessary, since _Bool is already in the reserved namespace
(as are _Complex and _Imaginary). Any C90 program using _Bool is in
trouble anyway.
...and that's why expressions such as `a < b < c' has the strange
(unexpected) semantics.

The semantics aren't strange at all *if* you look at them as being
built up from the semantics of the individual operators. "a + b + c"
is equivalent to "(a + b) + c". "a - b - c" is equivalent to
"(a - b) - c". The fact that the same rule applies to "a < b < c"
looks strange only because of the rather odd shorthand used in
mathematics. Programming languages tend to treat true/false values as
ordinary values; mathematics often treats them specially. The
approach used in programming languages is actually more uniform.

<OT>
Even in languages in which the native boolean type is non-numeric,
"a < b < c" tends not to mean what it means in mathematics. For
example, in Pascal the type Boolean is a special enumeration type (not
numeric) with values False and True and the relationship False < True.
The expression "a < b < c" is valid only if c is of type Boolean; the
result is equivalent to "(a < b) < c", and is either False or True.
</OT>
 
B

Ben Pfaff

August Karlstrom said:
It's strange that gcc (3.3.5) accepts _Bool declarations even when
compiled with the option `-std=c89':

_Bool is a valid extension to C89, because it cannot appear in
any strictly conforming C89 program.
 
M

Michael Wojcik

Because that's the way the syntax is defined. Not very useful. Just for
fun, try defining a grammar that doesn't allow a < b < c (that's easy),
then try defining a grammar that allows expressions like a <= b < c == d
<= e but not a <= b > c, then try defining the semantics like in
mathematics. It's fun.

In COBOL, Land of the Ad Hoc Grammar, you can use Abbreviated
Combined Relation Conditions, for example:

if b > a and < c

for the equivalent of the mathematical a < b < c. The standard (ISO
1989:2002 8.8.4.2.4) cautions against using NOT in ACRCs, as it may
lead to "results that are not intuitive". Their first example:

a > b AND NOT < c OR d means ((a > b) AND (a NOT < c)) OR (a NOT < d)

demonstrates nicely why too much syntatic sugar can cause decay.

Read more about it in ISO 1989:2002, which is a mere 879 pages.
 
C

Christian Bau

Keith Thompson said:
The semantics aren't strange at all *if* you look at them as being
built up from the semantics of the individual operators. "a + b + c"
is equivalent to "(a + b) + c". "a - b - c" is equivalent to
"(a - b) - c". The fact that the same rule applies to "a < b < c"
looks strange only because of the rather odd shorthand used in
mathematics. Programming languages tend to treat true/false values as
ordinary values; mathematics often treats them specially. The
approach used in programming languages is actually more uniform.

<OT>
Even in languages in which the native boolean type is non-numeric,
"a < b < c" tends not to mean what it means in mathematics. For
example, in Pascal the type Boolean is a special enumeration type (not
numeric) with values False and True and the relationship False < True.
The expression "a < b < c" is valid only if c is of type Boolean; the
result is equivalent to "(a < b) < c", and is either False or True.
</OT>

That's because the particular way the semantics is defined in C. It
could have been defined differently, for example: In a sequence "a +/- b
+/- c +/- d..." first all the operands are evaluated; if no operand has
pointer type, then all are converted to a common type using the usual
arithmetic promotions, then they are added in an unspecified order. Some
further tricky semantics is needed if any operands are pointers.

That would allow compilers to compute a + b + c as (a + b) + c or a + (b
+ c), whatever is more efficient; explicit brackets can be used to force
evaluation order. Likewise, "a <relop> b <relop> c <relop> d ..." could
have been defined in the mathematical way.
 
S

sudhz

Paminu said:
In math this expression:

(a < b) && (b < c)

would be described as:

a < b < c

But why is it that in C these two expressions evaluate to something
different for the same values of a, b and c?
In maths the two exressions/conditions are said to be "equivalent" -
but not "EQUAL".

They are equivalent in 'C' also - when you check for their equivalance.
 
P

pete

sudhz said:
In maths the two exressions/conditions are said to be "equivalent" -
but not "EQUAL".

They are equivalent in 'C' also
- when you check for their equivalance.

What are you talking about?

a < b < c
is true when c is 2 and false when c is 0.

(a < b) && (b < c)
always depends on the value of b and at least one other variable.
 
S

Simon Biber

Ben said:
_Bool is a valid extension to C89, because it cannot appear in
any strictly conforming C89 program.

Yes. It is unfortunate, though, that -std=c89 -pedantic -Wall is not
enough coercion to make GCC warn that using reserved identifiers like
_Bool is specifically undefined behaviour according to the standard. It
wouldn't be too hard to detect, I would think.

C89 4.8.9 "All external identifiers declared in any of the headers are
reserved, whether or not the associated header is included. All
external identifiers that begin with an underscore are reserved. All
other identifiers that begin with an underscore and either an
upper-case letter or another underscore are reserved. If the program
defines an external identifier with the same name as a reserved
external identifier, even in a semantically equivalent form, the
behavior is undefined."
 
T

Tim Rentsch

August Karlstrom said:
OK, you're right, sorry. The existence of the header file has made me
think it's not a native type. So _Bool is just an integer type that can
hold two values: 0 or 1, and it's called _Bool just to minimize the risk
of breaking existing programs, right?

The type _Bool is an integer type that can hold only two
values, those being 0 and 1. However, it's different
from other integer types in some important ways:

1. Assigning a zero value to a _Bool results in 0, and
assigning any non-zero value to a _Bool results in 1.
(Same for conversion.) No other integer type behaves
this way (in all implementations).

2. It's always legal to assign or convert a pointer
value to a _Bool.

In some sense the reason for _Bool is to have a type for
a variable that can capture exactly the value of a
control expression as would be used in an 'if', 'while'
or 'for'.
 
S

Simon Biber

Tim Rentsch wrote:
[...]
In some sense the reason for _Bool is to have a type for
a variable that can capture exactly the value of a
control expression as would be used in an 'if', 'while'
or 'for'.

Interesting -- in other words, converting a value to _Bool behaves
exactly as if the '!' (logical not) operator was applied twice.
 
K

Keith Thompson

Tim Rentsch said:
The type _Bool is an integer type that can hold only two
values, those being 0 and 1. However, it's different
from other integer types in some important ways:

1. Assigning a zero value to a _Bool results in 0, and
assigning any non-zero value to a _Bool results in 1.
(Same for conversion.) No other integer type behaves
this way (in all implementations).

It's all about conversion. The only way to assign a non-zero value to
a _Bool is to convert it to _Bool (unless it's already of type _Bool).
The conversion rules say that the result is always 0 or 1.
2. It's always legal to assign or convert a pointer
value to a _Bool.

I was about to disagree. Fortunately, I looked it up first; you can
assign a pointer value directly to a _Bool. Interesting.
In some sense the reason for _Bool is to have a type for
a variable that can capture exactly the value of a
control expression as would be used in an 'if', 'while'
or 'for'.

Yup.
 
S

S.Tobias

Keith Thompson said:
....

It's all about conversion. The only way to assign a non-zero value to
a _Bool is to convert it to _Bool (unless it's already of type _Bool).
The conversion rules say that the result is always 0 or 1.
# [#2] An object declared as type _Bool is large enough to
# store the values 0 and 1.

Doesn't that mean that _Bool _might_ hold other values as well?
The only method of obtaining such other value that comes to my mind
would be by reinterpreting bytes (eg. in a union, however such program
would not be s.c.).
 
T

Tim Rentsch

Keith Thompson said:
It's all about conversion. The only way to assign a non-zero value to
a _Bool is to convert it to _Bool (unless it's already of type _Bool).
The conversion rules say that the result is always 0 or 1.

Yes, but it's not only that the result is always 0 or 1.
An unsigned int bitfield of length one always holds either
0 or 1. But only _Bool maps any non-zero value to 1 and
only zero values to 0. There are conversions going on
in both cases (of bitfield and _Bool), but the conversion
rules for _Bool are different from those of any other
integer type.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,121
Latest member
LowellMcGu
Top