Charlie Gordon said:
E. Robert Tisdale said:
James said:
<posted & mailed>
Keep in mind that your compiler will probably turn all of these statements
into the same code:
i>>1;
i/=2;
i=i/2;
Let's try that. [...]
of all 4 tests, only 2 really make sense.
int fshift(int i) { return i >> 1; }
int fdiv(int i) { return i / 2; }
gcc optimizes i / 2 using shifts, but generates different code from i >> 1:
namely on intel chips, signed division by 2 is equivalent to :
(i + (i < 0)) >> 1
Sorry for not clipping the rest of the quote, this post was submitted
erroneously by a keyboard shortcut I wasn't aware of.
i / 2 <=> (i + (i < 0)) >> 1 assuming a ton of things that are mostly true
on the ia86 family :
- 2's complement
- signed division truncates toward 0
- signed shift duplicates the sign bit...
However, given this expression, gcc will generate a test and 2 JMPs.
You have to be very verbose to make it generate the same code :
return (i + (signed)((unsigned)i >> ((sizeof(i) * CHAR_BIT - 1)))) >> 1;
or the alternative:
return (i + ((i >> ((sizeof(i) * CHAR_BIT - 1))) & 1)) >> 1;
And still this assumes i is of type int. The expression must change for other
signed integral types.
Furthermore, for other simple divisions by a power of 2, gcc will generate a
test and 2 JMPs for the / operator.
But will generate linear (and potentially faster) code for these convoluted
expressions :
dividing by 4:
return (i + ((i >> ((sizeof(i) * CHAR_BIT - 1))) & 3)) >> 2;
dividing by 8:
return (i + ((i >> ((sizeof(i) * CHAR_BIT - 1))) & 7)) >> 3;
etc.
As a conclusion, it is more important to write readable code (using / and %) and
give the compiler every opportunity for optimisation by using or casting to
unsigned types. Yet this is a risky game:
- it is vain to rely on any particular compiler behaviour.
- even hand optimized code may prove slower.
- unsigned types have its own lot of problems.
- integer arithmetic is not necessarily 2's complement.
- >> has implementation defined semantics (can we list architectures where it
differs from ia86 ?).
- the java unsigned shift operator >>> was not added to the C standard (beats me
why).
- casting a signed arithmetic type to its unsigned version cannot be done
generically, reliably and portably.
- casting an unsigned arithmetic type to its signed version cannot be done
generically at all.
Such a can of worms!