signed OOP up-arrow comments Arduino hints and tips - Two's Complement(ation) - 4
Dual arduinos in control panel hdr

Fun and games with two's complements


If you made it through the signed vs unsigned hints page, your eyes may be going around in circles. But it should be starting to make sense - I hope.

Let's try to lay two's complement stuff to rest. To do that we need to know how it works inside...

(binary) 00000110 is (decimal) 6 Complement (invert) its bits:
    
     binary 0110  is  decimal 6
  

Complement (invert) the bits

             1001
  

and add 1

             1010  - the two's complement value for -6
  

This process is where the two's complement name came from! Neat!

Let's go the other way and turn a negative number into a positive one. Lets start with -2 (1110 in signed binary)

             1110   -2
             
             0001   invert the bits
             
             +  1   add one
            ------
             0010   and now it's a positive 2!
  

High level computer languages have a special shortcut (I think you'll recognize!). It's the minus sign!

  
             -   the unary minus sign
  

Because this is a fairly commmon thing to do, most cpus have a NEG (negate) instruction to do it instead of needing to used sevarl other instructions.

If you want, you can do it yourself in C/C++...

  
          int   x;  // signed variable
         
          x = 2;
          
          x = ~x;   // invert the bits in x
          
          x = x + 1;  // add 1
          
          Serial.print(x);   // it should print -2!
  

In C/C++ the '~' (tilde) operator means t0 bit-wise invert (also called a one's complement) a vlue. The inversion of 2 or 0000000000000010 is 1111111111111101.

Whether a variable is treated as unsigned or signed (two's complement) in an expression depends on the variable's types when they are declared.

    
       int  x;           // signed
       char y;           // signed
       long z;           // signed
       
       unsigned int ux;  // unsigned
       unsigned char uy; // unsigned
       unsigned long uz; // unsigned
    

Modern compilers should give you a least a warning if you attempt to compare or do arithmetic involving converting signed to unsigned values or vice versa.

     x = ux;   // ux may have a larger positive value than can be represented in x!
     
     if (u <= uy) {... // should this comparison be signed or unsigned?
     
     x = (int)ux;  // use casting to force unsigned to signed conversion
  

If you really want to melt your brain, there exists a three volume set of books on the fundamentals of computer theory written by Donald Knuth. In it he devotes quite a bit of space to selecting the base (radix) in which electronic computers operate. A number a radixes were proposed. One of the strangest was radix -3.

In that system you have a ones digit, a -3's digit, a 9's digit, a -27's digit, etc...

He also discuses other things about numbering systems. Iirc (it's been over 3 decades since I read these books) we have 60 seconds in a minute and 6 minutes in an hour, and also 60 is used for dividing degrees of latitude longitude because the ancients, Babylonian? astronomers didn't know how to work fractions and 60 can be evenly divided by 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, and 30. More factors than any other reasonably small number. Used because it resulted in fewer fractional answers when they were mapping the heavens.