[net.lang.c] Beware: Hackers

alan@allegra.UUCP (Alan S. Driscoll) (01/08/84)

From Roger Ferrel:

	What we see here with the:

		inttype *= 0.5;
			vs
		inttype = inttype * 0.5;

	is a weakness of most C compilers. It seems none of them handles
	float operations very well when there are mixed types.  Of course
	the above is a poor example since normally more efficent code would
	be generated by using:

	inttype >>= 1;  /* how is this for getting off the subject? */

From the C Reference Manual:

	The value of E1>>E2 is E1 right-shifted E2 bits.  The right shift
	is guaranteed to be logical (0-fill) if E1 is unsigned; otherwise
	it may be arithmetic (fill by a copy of the sign bit).

If the shift is logical, then negative values of "inttype" will give you
interesting results.


	Alan S. Driscoll
	AT&T Bell Laboratories

rpw3@fortune.UUCP (01/09/84)

#R:allegra:-218900:fortune:16200015:000:1388
fortune!rpw3    Jan  9 03:19:00 1984

"Those who learn nothing from history are doomed to repeat it"
	-- Santayana

"The only thing we learn from history is that we learn nothing
 from history" -- Hegel

"I know guys can't learn from yesterday... Hegel must be taking
 the long view" -- Brunner (as spoken by a character in Stand on Zanzibar)

Not only will "negative_int >> n"  give you a terribly wrong answer
(compared to "negative_int / (2**n)") if your machine uses logical shift,
but it will give you an "off by one" answer if you use arithmetic shift
(unless negative_int happens to be minus a power of 2 bigger than "n").

		(-5)/4 = -1	(correct integer division)

		(-5) >> 2 = -2	(arithmetic shift)

Many compilers which try to "optimize" this case also blow it (hmmm...,
you might try yours). As many have discovered in the past (the hard way,
after the compiler was in the field), the correct algorithm for optimizing
division by powers of two is (in C pseudo-code):

		if(x < 0)
		    x += 1;
		x = x "arith_right_shift" n ;	/* where the divisor is 2**n */

When the PDP-10 community was trying to make the move from the old F40
compiler to FORTRAN-10, this was a really hot issue. The new, "better"
compiler was the broken one (for a while).

Rob Warnock

UUCP:	{sri-unix,amd70,hpda,harpo,ihnp4,allegra}!fortune!rpw3
DDD:	(415)595-8444
USPS:	Fortune Systems Corp, 101 Twin Dolphins Drive, Redwood City, CA 94065

shah@fortune.UUCP (01/10/84)

#R:allegra:-218900:fortune:16200017:000:302
fortune!shah    Jan  9 19:37:00 1984

+-------
| "Those who learn nothing from history are doomed to repeat it"
|         -- Santayana
|
| "The only thing we learn from history is that we learn nothing
|  from history" -- Hegel
+-------

Conclusion:

The only thing that we learn from history is that we are
doomed to repeat it.

	Sigh....

henry@utzoo.UUCP (Henry Spencer) (01/11/84)

Rob Warnock observes:

  Not only will "negative_int >> n"  give you a terribly wrong answer
  (compared to "negative_int / (2**n)") if your machine uses logical shift,
  but it will give you an "off by one" answer if you use arithmetic shift
  (unless negative_int happens to be minus a power of 2 bigger than "n").

Actually, if the machine is using arithmetic shift the shift is arguably
right and the "true" division wrong.  The problem is that there is
no unique "right" definition of integer division for the case where the
remainder is nonzero. The basic problem is to define a function n div d
(where n and d are integers) that yields an ordered pair of integers <q, r>
such that q*d + r == n && |r| < |d|.  The problem is that there is more
than one such function.  The four most obvious possibilities can be summed
up in terms of their effects on a non-zero remainder:

	1. Remainder has same sign as dividend.
	2. Remainder has same sign as divisor.
	3. Remainder is always positive.
	4. Magnitude of remainder is minimized, with some decision rule
		for resolving ties (e.g. 5 div 2 == <2, 1> or <3, -1>).

3 is a bit odd.  4 is rounding, which is usual for floating-point
but odd for integers.  The major candidates are 1 and 2.  1 is the
Fortran approach, truncating the quotient towards 0.  Most languages
and most machines have followed Fortran's lead.  2 is "modulus division",
with the quotient truncated toward minus infinity.  This is the division
operation implemented by arithmetic shifts on a two's-complement machine.
Modulus division is possibly superior to Fortran division, in fact, and
some recent languages and one recent machine (the 16032) recognize this.
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

rpw3@fortune.UUCP (01/11/84)

#R:allegra:-218900:fortune:16200018:000:1338
fortune!rpw3    Jan 11 04:59:00 1984

I regret to say, my "fix" of the divide-a-negative-variable-by-a-
constant-power-of-two-using-a-shift has a bug in it (OOPS!).

As Gary Graunke (Tektronix) rightly points out, the number that should
be added to the variable before shifting (if the variable was negative)
is the power-of-two-minus-one, not just plain 'ol one.  Now you all
knew I knew all along, right? Well, I did.  (Just ask our local
compiler guys.) but (BLUSH!) I didn't proofread my note very well;
I was too busy with the fancy "learn from history" quotes.  (We will
see next time if I learn from history...)

Rob Warnock

UUCP:	{sri-unix,amd70,hpda,harpo,ihnp4,allegra}!fortune!rpw3
DDD:	(415)595-8444
USPS:	Fortune Systems Corp, 101 Twin Dolphins Drive, Redwood City, CA 94065

+----------------extract from mail--- (also posted to net.lang.c // eventually)
| From allegra!tektronix!tekchips!garyg Tue Jan 10 18:47:51 1984
|
| The incorrect algorithm is...
| 
| 		int x;
| 		unsigned int n;
| 		/* replace x/(2^n), n>=0 by ... */
| 		if(x < 0)
| 		    x += 1;
| 		x = x "arith_right_shift" n ;	/* where the divisor is 2^n */
| 
| The correct algorithm is...
| 
| 		int x;
| 		unsigned int n;
| 		/* replace x/(2^n), n>=0, 2^n-1 <= maxint by ... */
| 		if(x < 0)
| 		    x += 2^n-1;
| 		x = x "arith_right_shift" n ;	/* where the divisor is 2^n */
+---------------

usenet@abnjh.UUCP (usenet) (01/12/84)

>> The four most obvious possibilities can be summed
>> up in terms of their effects on a non-zero remainder:
>> 
>> 	1. Remainder has same sign as dividend.
>> 	2. Remainder has same sign as divisor.
>> 	3. Remainder is always positive.
>> 	4. Magnitude of remainder is minimized, with some decision rule
>> 		for resolving ties (e.g. 5 div 2 == <2, 1> or <3, -1>).
>> 
>> 3 is a bit odd.  

Not at all, 3 is what most mathematicians would say is *the* correct
way to divide two integers.  It is most convenient for doing modulo
arithmetic.  The result of doing n mod m is always less than m and
greater or equal to zero.

Rick Thomas