home | list info | list archive | date index | thread index

Re: [OCLUG-Tech] twos complement & signed types ??

Perhaps one thing that is missing in this conversation is the fact that
when the ALU adds one to any memory value, the same thing always happens.
Basic integer math doesn't care about sign.  There is no concept, at the
ALU level, of "signed" or "unsigned".  It's all just bits being added.

In four bits (binary):

0000
    plus one is
0001
    plus one is
0010
    plus one is
0011
    plus one is
0100
    plus one is
0101
    plus one is
0110
    plus one is
0111
    plus one is (also causes the overflow flag to be set in the CPU)
1000
    plus one is
1001
    plus one is
1010
    plus one is
1011
    plus one is
1100
    plus one is
1101
    plus one is
1110
    plus one is
1111
    plus one is (also causes carry and overflow flags to be set in the CPU)
0000
    plus one is
0001
    ...etc...

For addition (or subtraction) it doesn't matter if the variable is signed
or unsigned - when you say "x = x + 1" the ALU just adds one to the bits
in memory and sets some CPU flags.  Signed, unsigned, same thing.

In C language with 32-bit integers:

    signed int sx = 2000000000;
    signed int sy = 2000000001;
    signed int sz = sx + sy;

    unsigned int ux = 2000000000;
    unsigned int uy = 2000000001;
    unsigned int uz = ux + uy;

    printf("sz is 0x%x which could be %d or %u\n", sz, sz, sz);
    printf("uz is 0x%x which could be %d or %u\n", sz, uz, uz);

Output (first output is in hexadecimal):

    sz is 0xEE6B2801 which could be -294967295 or 4000000001
    uz is 0xEE6B2801 which could be -294967295 or 4000000001

The hex value 0xEE6B2801 when turned into 32-bit binary looks like this:

    11101110011010110010100000000001

See - absolutely no difference in the two bit patterns in memory.  The bit
patterns in memory for signed and unsigned math are the same; because,
there is no difference in what the ALU does.  The ALU doesn't care.
Adding/subtracting don't care about sign.

As you see above, once we have some bits in memory, we can tell our
program to interpret the bits in either an unsigned manner ("%u" - all
the bit patterns represent non-negative numbers) or in a signed manner
("%d" - about half of the bit patterns will be displayed as negative,
with leading minus).

Signed/unsigned doesn't matter in math.  Where a signed/unsigned
declaration plays a big part is in *comparing* numbers (and in
bit-shifting; but, save that for another day).  If we compare the bit
pattern 0xEE6B2801 (above) with zero, is the bit pattern less than zero
or much greater than zero?  *That* answer depends on whether we declared
the memory location (the variable) to be signed or unsigned.

If we declare a 32-bit variable as unsigned, numeric comparisons
treat all 4,294,967,296 bit patterns from zero up to 0xFFFFFFFF
(11111111111111111111111111111111) as non-negative numbers (zero to
4,294,967,295 for 32-bit numbers).  Comparisons (unsigned) will never
say any of these patterns are less than zero.

If we declare a 32-bit variable as signed, we are saying to our compiler
that numeric comparisons using that variable will treat about half of
those 4,294,967,296 bit patterns as negative numbers.  Any bit patterns
in signed variables that have the leftmost bit (the sign bit) set,
will be interpreted and compared as "less than zero".

More C code:

    if ( sz < 0 )
	    printf("sz is less than zero\n");
    else
	    printf("sz is not less than zero\n");

    if ( uz < 0 )
	    printf("uz is less than zero\n");
    else
	    printf("uz is not less than zero\n");

Output:

    sz is less than zero
    uz is not less than zero

Remember that sz and uz contain exactly the same bit patterns!

Because we declared the sz variable to be signed, the code generated
by the compiler to test whether the bit pattern in sz is less than zero
tests the sign bit (leftmost bit) on the sz memory, notices that is is on
('1') and declares the bit pattern as "less than zero" (signed).  All the
bit patterns with the sign bit on will be interpreted as negative numbers.

Because we declared the uz variable to be unsigned, the code generated
by the compiler to test whether the bit pattern in uz is less than zero
has no work to do at all - unsigned numbers are never less than zero.
The compiler arranges to print:  "uz is not less than zero"

As a programmer, declaring variables as signed/unsigned is just a
convenient way of having the compiler conspire with us to treat half
the bit patterns as "less than zero".  For math (adding/subtracting),
the declaration of signed/unsigned doesn't make any difference.
For comparisons (and bit-shifting), it does.

See also "odometer math" in:

http://elearning.algonquincollege.com/coursemat/pincka/dat2343/lectures.f03/04-Unsigned-Binary-Encoding.htm
http://elearning.algonquincollege.com/coursemat/pincka/dat2343/lectures.f03/06-Signed-Binary-Encoding.htm

-- 
| Ian! D. Allen  -  idallen [ at ] idallen [ dot ] ca  -  Ottawa, Ontario, Canada
| Home Page: http://idallen.com/   Contact Improv: http://contactimprov.ca/
| College professor (Open Source / Linux) via: http://teaching.idallen.com/
| Defend digital freedom:  http://eff.org/  and have fun:  http://fools.ca/