[comp.lang.c] doubles used as booleans

kremer@cs.odu.edu (Lloyd Kremer) (03/31/89)

	Can a double be used directly as a boolean?

	A few years ago, when DOS C compilers were in their infancy, I
	was hand optimizing the code of one of our products so that it
	would still fit in the standard 256K of an IBM XT.
	(I said it was a few years ago :-)  )

	The optimizing in the compilers I was using was minimal, and I
	found that substitutions such as 'if(i)' instead of 'if(i != 0)',
	and 'if(!n)' in place of 'if(n == 0)' produced smaller code for
	integral types.  When I tried to extend this technique to floating
	types where the potential gains were even greater (floating point
	comparisons were expensively implemented), the code broke completely.

	For example, if I had

		double a, b;
		.
		.
		if ( a && b )  /* meaning if(a != 0.0 && b != 0.0) */
			statement;

	the entire code fragment silently miscompiled.

	But if I changed the condition to

		( !!a && !!b )
	or to
		( !( !a || !b ) )

	it compiled fine!

	Even K&R 1 states that the ! operator can be applied to any type,
	so I believe the latter examples are required to work, but what
	about the first one?  Would these compilers be officially broken
	by today's standards?

	Pray, what sayeth the pANS?

	If it means anything, doubles were implemented as IEEE 754
	64-bit reals, where 0.0 was represented by a zero-bit pattern.
	Maybe that's the only reason the latter cases worked!  :-)


					Lloyd Kremer
					Brooks Financial Systems
					{uunet,sun,...}!xanth!brooks!lloyd

bowles@eris.berkeley.edu (Jeff A. Bowles) (03/31/89)

In article <8269@xanth.cs.odu.edu> kremer@cs.odu.edu (Lloyd Kremer) writes:
>
>	Can a double be used directly as a boolean?

I don't have a copy of ANSI, but you don't need it for part of the answer:
"Are you really, absolutely sure you wanna compare a floating-point number
to ANYTHING else, just to see if the numbers are EQUAL?"

If you're interested in if a floating-point variable is equal to another
floating-point variable, or if it's equal to zero, well, umm, think very
carefully about whether you're getting ready to shoot yourself in the
foot.

It's the same type of thing that makes 3*(1.0/3.0) not equal to 1.0....

	Jeff Bowles

ps. Comparing to some small amount, like  "abs(x) < 0.00001", doesn't do
a lot better when the calculations are smaller than that, either.

guy@auspex.auspex.com (Guy Harris) (03/31/89)

>	The optimizing in the compilers I was using was minimal, and I
>	found that substitutions such as 'if(i)' instead of 'if(i != 0)',
>	and 'if(!n)' in place of 'if(n == 0)' produced smaller code for
>	integral types.  When I tried to extend this technique to floating
>	types where the potential gains were even greater (floating point
>	comparisons were expensively implemented), the code broke completely.

Proof positive that lack of memory sometimes produces
less-than-wonderful compilers - I would hope any *modern* non-toy
compiler would not give you any better code for "if (i)" than it gives
for "if (i != 0)".

>	Pray, what sayeth the pANS?

	3.1.2.5 Types

	...

	     Integral and floating types are collectively called
	"arithmetic types".  Arithmetic types and pointer types are
	collectively called "scalar types".

	3.2.1.5 Usual arithmetic conversions

	     Many binary operators that expect operands of arithmetic
	type cause conversions and yield result types in a similar way. 
	... This pattern is called the "usual arithmetic conversions":

		First, if either operand has type "long double", the
		other operand is converted to "long double".

		Otherwise, if either operand has type "double", the
		other operand is converted to "double".

		Otherwise, if either operand has type "float", the other
		operand is converted to "float".

	3.3.8 Relational operators

	...

	Semantics

	     If both of the operands have arithmetic type, the usual
	arithmetic conversions are performed.

	3.3.9 Equality operators

	...

	Semantics

	     The == (equal to) and the != (not equal to) operators are
	analogous to the relational operators except for their lower
	precedence.  Where the operands have types and values suitable
	for the relational operators, the semantics detailed in section
	3.3.8 apply.

	3.3.3.3 Unary arithmetic operators

	Constraints

	     The operand ... of the ! opearator (shall have) scalar
	type.

	Semantics

	     The result of the logical negation operator ! is 0 if the
	value of its operand compares unequal to 0, 1 if the value of
	its operand compares equal to 0.  The result has type "int". 
	The expression "!E" is equivalent to "(0==E)".

	3.3.13 Logical AND operator

	Constraints

	     Each of the operands shall have scalar type.

	Semantics

	    The && operator shall yield 1 if both of its operands
	compare unequal to 0, otherwise it yields 0.  The result has
	type "int".

	3.3.14 Logical OR operator

	(same thing, only it yields 1 if *either* of its operands
	compare unequal to 0)

	3.3.15 Conditional operator

	Constraints

	     The first operand shall have scalar type.

		...

	Semantics

	     The first operand  is evalueate; there is a sequence point
	after its evaluation.  The second operand is evaluated only if
	the first operand compares unequal to 0; the third operand is
	evaluated only if the first compares equal to 0; the value of
	the second or third operand (whichever is evaluated) is the
	result.

	3.6.4.1 The "if" statement

	Constraints

	     The controlling expression of an "if" statement shall have
	scalar type.

	Semantics

	     In both forms, the first substatement is executed if the
	expression compares unequal to 0.

When you combine all that stuff, it dictates, among other things, that

	double d;
	if (d)

had better be equivalent to

	double d;
	if (d != 0.0)

or

	if (d != 0)

given that the "0" in the latter case is converted to "0.0", and that

	double a, b;
	.
	.
	if ( a && b )  /* meaning if(a != 0.0 && b != 0.0) */
		statement;

had better be interpreted precisely the way you state it.

>	If it means anything, doubles were implemented as IEEE 754
>	64-bit reals, where 0.0 was represented by a zero-bit pattern.
>	Maybe that's the only reason the latter cases worked!  :-)

One hopes that if there ever were compilers dumb enough to think that
the "if" statement worked by testing whether the all the bits of the
value in the parentheses were zero, those compilers are now toast and
their authors have repented of their errors.

henry@utzoo.uucp (Henry Spencer) (04/01/89)

In article <8269@xanth.cs.odu.edu> kremer@cs.odu.edu (Lloyd Kremer) writes:
>	Can a double be used directly as a boolean?

Any "scalar" type -- an arithmetic or pointer type -- can.

>	The optimizing in the compilers I was using was minimal, and I
>	found that substitutions such as 'if(i)' instead of 'if(i != 0)',
>	and 'if(!n)' in place of 'if(n == 0)' produced smaller code for
>	integral types...

Talk about lazy compilers...

>	[compilers that balk at doing implicit comparison against 0 for double]
>	... Would these compilers be officially broken by today's standards?

Yes.  They've been officially broken by the prevailing de-facto standards
all along, as well.
-- 
Welcome to Mars!  Your         |     Henry Spencer at U of Toronto Zoology
passport and visa, comrade?    | uunet!attcan!utzoo!henry henry@zoo.toronto.edu