From: utzoo!decvax!cca!chris.umcp-cs@Udel-Relay@sri-unix
Newsgroups: net.unix-wizards
Title: Floating Point and Other Such Mysteries
Article-I.D.: sri-unix.3338
Posted: Sat Sep 18 00:54:18 1982
Received: Wed Sep 22 10:33:00 1982

From:     Chris Torek 
Date:     14 Sep 82 4:07:30-EDT (Tue)
Whitesmith's C for 8080s has an interesting compromise.  If you use

	float x, y; x = y + .01;

they convert y to double, add .01 (double), and convert the result to
float and stuff it in x.  But if you use

	float x, y; x = .01; x += y;

they do the operation in single precision!  This works for 'char' too.
Anyway, it seems to me that the rules for arithmetic could be revised
as follows:

1. Before function calls, all chars/floats are widened to ints/doubles.
   There is no such thing as a float or char function (though of course
   there ARE float *, char *).
2. There are such things as float constants.  FP constants are double
   until the entire expression is known to be at most float, excluding
   any FP constants.
3. Operations are done in the highest precision of all the operands,
   INCLUDING THE LVALUE on the left of the asgop.

Examples:
	char	c1, c2, c3;
	int	i1;
	float	f1;
	double	foo();

	c1 = 'x' + 'z';		/* compile time, CHAR */
	c2 = c1 + c3;		/* done in CHAR */
	c3 = c1 + bar('y');	/* done in INT */

	i1 = ;	/* INT of course */
	f1 = 3.14159265358979;	/* FLOAT, though constant is DOUBLE */
	f1 *= 6;		/* FLOAT */
	f1 *= (double) .3;	/* still FLOAT */
	f1 *= foo();		/* DOUBLE */

The reason for #3 above is:

	char c1, c2; int i;

	i = c1 + c2;		/* don't want to add these as
				   CHAR because result is INT */

If c1+c2 were evaluated as CHAR, '~'+'~' would yeild a negative
result due to sign extension.  (It yeilds 252 in existing C.)

Doing something like

	int i; char c1, c2;

	i = c = c1 + c2;	/* i = (int)(char)  done here */

WOULD yeild a negative result.


There is an easy way around all of this, though, which no one
seems to have mentioned:  use pointers EVERYWHERE!  No longer
do you use "double cos();"; intstead you have

	cos0 (op, res) float *op, *res;	{return;}
	cos1 (op, res) float *op; double *res; {return;}
	cos2 (op, res) double *op; float *res; {return;}
	cos3 (op, res) double *op, *res; {return;}

(just like FORTRAN!).  Yes this is grungy but it will work
in all existing C compilers and it is portable, and not really
all that hard.  If you really want efficiency it's probably better
to pass pointers (which are nice handy machine word size) than
big hairy 8 byte floating point numbers.  (Of course, you
need to write a whole new math library...)