defining a boolean type

R

Rod Pemberton

Keith Thompson said:
Anything other than 0 is true(adjective), but true(noun) is 1.

You can only define a single value as TRUE (or true). The most
obvious value to use is 1.

Is it? Isn't _obvious_ it should -1? (-1 a.k.a. all bits set)

IMO, false is zero, true should generate -1, and non-zero values should be
accepted true. When so, then bitwise operations can be used to perform
logical operations. This doesn't affect affect C programmers directly.
But, it does affect the implementation of C, since few (or no) assembly
languages directly support logical operations or boolean operations. They
typically only support bitwise operations.
It's the value yielded by the "!",
relational, equality, "&&", and "||" operators, and it's the value
used in C99's <stdbool.h>.

That's true. But, using 1 as the only valid value "true" means you can only
use it within logical operations. What if you forget and use it with a
binary operation? Shouldn't a value of true be chosen to work with _both_
binary and logical operations? Wouldn't that be wise?
The only valid value for "false" is 0, and the only really sensible
value for "true" is 1.

I don't agree. I want my value for "true" to work correctly for both binary
and logical operations as a non-false value.


#include <stdio.h>

#define FALSE 0

/* enable TRUE you wish to test */
/* disable the others */

#if 0
#define TRUE 1
#endif

#if 0
#define TRUE (!FALSE)
#endif

#if 1
/* works for both */
#define TRUE -1
#endif

int main(void)
{
unsigned int i;

for(i=0;i<3;i++)
{

printf("\nFor %d:\n",i);
if(i&FALSE)
printf("FALSE fails as binary-false.\n");
else
printf("FALSE succeeds as binary-false.\n");

if(i&&FALSE)
printf("FALSE fails as logical-false.\n");
else
printf("FALSE succeeds as logical-false.\n");

if(i!=0)
{
if(i&TRUE)
printf("TRUE succeeds as binary-true.\n");
else
printf("TRUE fails as binary-true.\n");

if(i&&TRUE)
printf("TRUE succeeds as logical-true.\n");
else
printf("TRUE fails as logical-true.\n");
}

}

return(0);
}

HTH,


Rod Pemberton
 
S

Seebs

Is it?
Yes.

Isn't _obvious_ it should -1? (-1 a.k.a. all bits set)

Not really "obvious". Interestingly, the one other person I've seen argue
that was Edward Nilges. :)

Here's my thought: Consider a single bit. It's either zero or one.

The non-zero value is 1. Otherwise, you have the problem that

((unsigned short) true == (int) true)

might be untrue.
That's true. But, using 1 as the only valid value "true" means you can only
use it within logical operations. What if you forget and use it with a
binary operation? Shouldn't a value of true be chosen to work with _both_
binary and logical operations? Wouldn't that be wise?

I don't think it works out that way. If you pick -1, then
(x & true)
is always true if x is non-zero -- but
(x & true) & (y & true)
might not be non-zero even if both x and y are non-zero.

So I don't much like the attempt to make the first work, since if the first
works, you might mistakenly expect the second to work.
I don't agree. I want my value for "true" to work correctly for both binary
and logical operations as a non-false value.
if(i&TRUE)
printf("TRUE succeeds as binary-true.\n");

That doesn't make any sense, though. How is "i & TRUE" being non-zero making
TRUE a "binary-true"?

With the usual value, if (x & true) is non-zero, (x & true) == true. With
yours, it isn't. I like that less.

-s
 
K

Keith Thompson

Rod Pemberton said:
Is it? Isn't _obvious_ it should -1? (-1 a.k.a. all bits set)

Yes, and no, respectively.
IMO, false is zero, true should generate -1, and non-zero values should be
accepted true. When so, then bitwise operations can be used to perform
logical operations. This doesn't affect affect C programmers directly.
But, it does affect the implementation of C, since few (or no) assembly
languages directly support logical operations or boolean operations. They
typically only support bitwise operations.

False is zero, and non-zero values are true, so you're at least two
thirds in agreement with the language.
That's true. But, using 1 as the only valid value "true" means you can only
use it within logical operations.

Yes, that's what it's for.
What if you forget and use it with a
binary operation?

Then you've got a problem. So don't do that.
Shouldn't a value of true be chosen to work with _both_
binary and logical operations? Wouldn't that be wise?

I don't think so. Binary and logical operations are different things,
used in different contexts.
I don't agree. I want my value for "true" to work correctly for both binary
and logical operations as a non-false value.

Then feel free to define it that way in your own language. If you
insist on using -1 as the canonical true value, you'll eventually run
into problems with code that uses 1 as the canonical true value.

On the other hand, if you're sufficiently careful, it doesn't matter
which non-zero value you use to represent truth.

[code snipped]

/* Determine how many elements of arr are negative: */
int count = 0;
for (i = 0; i < MAX; i ++) {
count += (arr < 0);
}
 
N

Nick Keighley


false is NIL anything else is true

Not really "obvious".  Interestingly, the one other person I've seen argue
that was Edward Nilges.  :)

I'm sure I've come across a language where this was correct (I nearly
wrote "true" there!) but I can't remember which one. BCPL?

Ah yes!
http://www.fh-jena.de/~kleine/history/languages/Richards-BCPL-ReferenceManual.pdf

section 4.4

"The Rvalue of -true- is a bit pattern entirely composed of ones; the
Rvalue of -false- is zero."


<snip>
 
R

Rod Pemberton

Keith Thompson said:
False is zero, and non-zero values are true, so you're at least two
thirds in agreement with the language.

Actually, if you reread, I'm 100% in agreement with the language:

1) "IMO, false is zero, ..." That's true, and you're in agreement: "False is
zero, ..."
2) "..., and non-zero values should be accepted [as] true." That's true,
and you're in agreement. "... non-zero values are true, ..."

Those two are all the language requires for agreement. My extra condition
doesn't restrict agreement with the language.
If you
insist on using -1 as the canonical true value, you'll eventually run
into problems with code that uses 1 as the canonical true value.

If you insist on using 1 as the canonical true value, you'll eventually run
into problems with code that uses -1 as the cononical true value. It's only
when _everyone_ has agreed to use a single value for the canonical true
value that no one will have problems. But, of course, using either value as
a canonical true is incorrect. Isn't it? Using any specific value for
true, other than testing for non-zero, should be though of as incorrect,
shouldn't it? It is only correct in situations where both true and false
_have been defined_ to be specific values, such as booleans. Yes?
On the other hand, if you're sufficiently careful, it doesn't matter
which non-zero value you use to represent truth.

"If you're sufficiently careful", could be said about gets() and ptrdiff_t
and whatever else too.


Rod Pemberton
 
R

Rod Pemberton

Seebs said:
Not really "obvious".

I was discretely pointing out KT still has no experience with assembly or
the implementation of C using assembly. All the world is ADA...
Interestingly, the one other person I've seen argue
that was Edward Nilges. :)

That just means you haven't used other languages. FORTH used 0 and 1 for
false and true, respectively for the first two standard versions: fig-FORTH
and FORTH-77. In FORTH-83, they converted to 0 and -1 for false and true,
respectively. That was 1983: 27 years ago. The reason was the same as for
assembly. Logical operations must be constructed from bitwise operations in
most assembly.
Here's my thought: Consider a single bit. It's either zero or one.

The non-zero value is 1. Otherwise, you have the problem that

((unsigned short) true == (int) true)

might be untrue.

Did I miss something? C has that problem currently. So, what's your point?

True is non-zero. If true as non-zero is larger than a C byte, true could
be truncated via a cast from a non-zero value to a zero value. It's only if
you use a #define to specify a value for TRUE that can't be truncated that
"((unsigned short) true == (int) true)" will not be untrue.
I don't think it works out that way. If you pick -1, then
(x & true)
is always true if x is non-zero -- but
Correct.

(x & true) & (y & true)
might not be non-zero even if both x and y are non-zero.

For -1 as a true value, true. But, (x&true) and (y&true) have both worked
properly using true with a binary operation. It's the later operation,
which doesn't use true, that fails. This is no different than x&y resulting
in zero for certain non-zero binary values.
So I don't much like the attempt to make the first work, since if the first
works, you might mistakenly expect the second to work. ....



That doesn't make any sense, though. How is "i & TRUE" being non-zero making
TRUE a "binary-true"?

From the binary and (&) ... meaning that TRUE works as true with a binary
operation. Using a value of 1 for true will fail to result in true with a
binary and (&) for even values of 'i'.
With the usual value, if (x & true) is non-zero, (x & true) == true.

(x&true) results in true only for odd values of x. So, (x&true)==true only
reports odd's (non-portably). It produces a zero result for half.

If true is non-zero and x is non-zero, wouldn't you like a non-zero result
for (x&true)? Just like (x&&true)?
With the usual value, if (x & true) is non-zero, (x & true) == true.
With yours, it isn't.

However, I'm not sure why anyone would do a binary comparison of a result
with true when you can test this directly via an if()...


RP
 
K

Keith Thompson

Rod Pemberton said:
I was discretely pointing out KT still has no experience with assembly or
the implementation of C using assembly. All the world is ADA...

Assuming "KT" refers to me, I don't know what you're talking about,
or why. This isn't about what experience I personally may or may
not have. As for ADA, the correct spelling is Ada, and your remark
is neither true nor relevant.
 
N

Nick

Rod Pemberton said:
Did I miss something? C has that problem currently. So, what's your point?

It does if the "true" in the line of code means "an arbitrary value that
is true". But Seebs' point is about the value you've chosen to define
the symbol "true" as. If it's 1, as he's arguing, then whatever you
cast it to will still be 1. If it's -1, as you were arguing, then these
two casts can yield values that don't compare equal.
 
N

Nick Keighley

I was discretely pointing out KT still has no experience with assembly or
the implementation of C using assembly.  All the world is ADA...

so discretely it completely passed *me* by. I havn't a clue what
Keith's assemble experience is. I don't see why heavy use of assembly
would lead to -1 being the "obvious" choice for -1. What's Ada got to
do with price of fish?
That just means you haven't used other languages.  

there's plenty of langaues that don't use -1 for true. Algol, pascal,
FORTRAN, Lisp, scheme for starters.

[...]  In FORTH-83, they converted to 0 and -1 for false and true,
respectively.  
[...]

assembly.  Logical operations must be constructed from bitwise operations in
most assembly.

you'll have to explain. If I want to generate true I could invert all
the bits of false
xora FF

but why is that any easier than
xora 1

If I use single bits for booleans I can multiple booleans into a
single byte (I've generated morse on multiple channels this way). You
way precludes that.

Assembly *increses* your flexibility in low level data representation,
so why limit yourself this way?

Did I miss something?  C has that problem currently.  So, what's your point?

I'm left equally baffled. "it hurts when I do this, doctor!" "so don't
do that"


I don't see why. If its logically a boolean then use it as a boolean.
If it's a bunch of bits use it as a bunch of bits.
For -1 as a true value, true.  But, (x&true) and (y&true) have both worked
properly using true with a binary operation.  It's the later operation,
which doesn't use true, that fails.  This is no different than x&y resulting
in zero for certain non-zero binary values.

it's the fundamental flaw in confusing boolean with bunch-of-bits.

why?


However, I'm not sure why anyone would do a binary comparison of a result
with true when you can test this directly via an if()...

nor me, but it seems to be widespread. If only C had had a proper
boolean type from day 1... And a null keyword. And proper function
prototypes (what we have now should have been in K&R). Secretly I want
C to be pascal.
 
F

Flash Gordon

Rod said:
I was discretely pointing out KT still has no experience with assembly or
the implementation of C using assembly. All the world is ADA...

I do have experience of a number of assembly languages...

All the ones I've used had tests for the accumulator being zero or non-zero.
That just means you haven't used other languages. FORTH used 0 and 1 for
false and true, respectively for the first two standard versions: fig-FORTH
and FORTH-77. In FORTH-83, they converted to 0 and -1 for false and true,
respectively. That was 1983: 27 years ago. The reason was the same as for
assembly. Logical operations must be constructed from bitwise operations in
most assembly.

Pascal uses 0 for false and 1 for true. At least, all the version I've
used did.
Did I miss something? C has that problem currently. So, what's your point?

You are guaranteed that ((unsigned short)(1==1) == (int)(1==1)) in C.
With your proposal you would not have this guarantee. I though it
obvious the comment was referring to the cannocical value of true (i.e.
the value that boolean operators yield for true)
True is non-zero. If true as non-zero is larger than a C byte, true could
be truncated via a cast from a non-zero value to a zero value. It's only if
you use a #define to specify a value for TRUE that can't be truncated that
"((unsigned short) true == (int) true)" will not be untrue.

See above.
For -1 as a true value, true.

I.e. for what you are recommending you get this "problem".
But, (x&true) and (y&true) have both worked
properly using true with a binary operation. It's the later operation,
which doesn't use true, that fails. This is no different than x&y resulting
in zero for certain non-zero binary values.

I.e. using binary operators as if they are logical operators does not
work, so there is no point in trying to bend the language to make it
appear to work some of the time.
From the binary and (&) ... meaning that TRUE works as true with a binary
operation. Using a value of 1 for true will fail to result in true with a
binary and (&) for even values of 'i'.

So don't do that. Binary operators are not designed to be used as
logical operators.
(x&true) results in true only for odd values of x. So, (x&true)==true only
reports odd's (non-portably). It produces a zero result for half.

If true is non-zero and x is non-zero, wouldn't you like a non-zero result
for (x&true)? Just like (x&&true)?

No, why would I? It doesn't make sense to try and use binary operators
for logical tests or logical values as operands to binary operators.
However, I'm not sure why anyone would do a binary comparison of a result
with true when you can test this directly via an if()...

In other words if you use the language as it is you don't have any of
the problems you are trying to solve!
 
D

David Thompson

Yes, and no, respectively.
I have to dissent, at least in in principle. IME there are reasonable
arguments, and use cases, for both +1 and -1 aka ~0u, so I don't think
either is obvious a priori. For C, of course, the choice was made long
ago, and swimming against the tide now is just asking for trouble.

As noted, BCPL chose ~0 (rather its equivalent syntax, which I
forget); but it had only one data size and didn't deal with promotion.
And as noted, FORTH did for the standard, although AFAICT FORTH has
even greater profusion of not-quite-standard dialects than C does.
/* Determine how many elements of arr are negative: */
int count = 0;
for (i = 0; i < MAX; i ++) {
count += (arr < 0);
}


OTOH APL used +1 partly to (intentionally) support a more convenient
form of things like this example. And PL/I did kind-of accidentally;
actually it uses BIT or more precisely BIT(1), but that (silently)
converts to integer 0 or +1, which can be used in arithmetic.

And FWIW in languages with user-defined enumerations, they usually are
represented at least by default with the first name 0 and the second 1
etc., so enum ( false, true ) 'naturally' gives you +1, and arguably
more intuitive enum ( true, false ) gives you a big pain.
 
K

Keith Thompson

David Thompson said:
I have to dissent, at least in in principle. IME there are reasonable
arguments, and use cases, for both +1 and -1 aka ~0u, so I don't think
either is obvious a priori. For C, of course, the choice was made long
ago, and swimming against the tide now is just asking for trouble.

Sure, in principle there's nothing really wrong with representing
false as 0 and true as -1 (or as 37 if you like). But as you say,
C made the choice of 0 and 1 long ago, in the values yielded by a
number of operators, and more recently in the values chosen for the
As noted, BCPL chose ~0 (rather its equivalent syntax, which I
forget); but it had only one data size and didn't deal with promotion.
And as noted, FORTH did for the standard, although AFAICT FORTH has
even greater profusion of not-quite-standard dialects than C does.
/* Determine how many elements of arr are negative: */
int count = 0;
for (i = 0; i < MAX; i ++) {
count += (arr < 0);
}


OTOH APL used +1 partly to (intentionally) support a more convenient
form of things like this example. And PL/I did kind-of accidentally;
actually it uses BIT or more precisely BIT(1), but that (silently)
converts to integer 0 or +1, which can be used in arithmetic.


On the other hand, if C didn't guarantee that "<" yields 0 or 1, the
above could be written as:

int count = 0;
for (i = 0; i < MAX; i ++) {
count += (arr < 0 ? 1 : 0);
}

or:

int count = 0;
for (i = 0; i < MAX; i ++) {
if (arr < 0) {
count ++;
}
}

and there are arguments to be made that either of them is better
style anyway.
And FWIW in languages with user-defined enumerations, they usually are
represented at least by default with the first name 0 and the second 1
etc., so enum ( false, true ) 'naturally' gives you +1, and arguably
more intuitive enum ( true, false ) gives you a big pain.

At least one language (Ada) explicitly defines its Boolean type as an
enumeration type.
 
F

Flash Gordon

Keith said:
At least one language (Ada) explicitly defines its Boolean type as an
enumeration type.

I've always thought that false being less than true (false<true) has a
certain linguistic elegance, and also provides an easy way to remember
which has what value.
 
N

Nick Keighley

I've always thought that false being less than true (false<true) has a
certain linguistic elegance, and also provides an easy way to remember
which has what value.

oh my god! he's muddled his axiomisations!
 
N

Nick Keighley

Nice page.

I have no idea how wiki happens, yet I consider it this huge advance in
information evangelism.

it would have saved an hour of my life. Instead of spending an hour
scribbling increasingly arcane stuff on a blackboard the lecturer
could have just said "read this wiki page". Of course wiki hadn't
been invented then, though just putting the title up at the beginning
would have helped a lot. It's all a bit of shame when you don't find
out what the reason for it all is until you get to the end... Though
of course it's one lecture I'll always remember.
 

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,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top