Computer Science 15-112, Fall 2012
Class Notes:  Data and Expressions


  1. Required Reading
  2. Summary of Numeric and Boolean Operators
  3. Types Affect Semantics
  4. Operator Precedence
  5. Integer division
  6. The Modulus or Remainder operator (%)
  7. Integer vs Floating-point division
  8. Approximate values of floating-point numbers
  9. "almost equal" comparison of doubles
  10. Short-circuit evaluation
  11. Truth Tables for Testing Logical Equivalence
  12. Bitwise Operators
  13. Bit Masks
  14. Conditional Expressions
  15. Two's Complement
  16. Boolean Logic via Arithmetic
  17. Some Practice

Data and Expressions

  1. Required Reading
    1. Excerpts from: Python Standard Library, Ch 5 (Built-In Types), 5.1 through 5.4
    2. Excerpts from: Python Standard Library, Ch 2 (Built-In Functions)
      (just these functions: abs, bin, bool, complex, dir, eval, float, help, hex, int, long, max, min, oct, pow, round, type)
    3. Excerpts from: Python Language Reference, Ch 3.2 (The standard type hierarchy), just until "Sequences"
    4. Python Language Reference, Ch 5.14 (Evaluation order)
    5. The Python Tutorial, Ch 3.1.1 (Numbers)
       
  2. Summary of Numeric and Boolean Operators
     
    Category Operators
    Arithmetic +, -, *, /, //, **, %, - (unary), + (unary)
    Relational <. <=, >=, >, ==, !=, <>
    Bitwise <<, >>, &, |, ^, ~
    Assignment +=, -=, *=, /=, //=, **=, %=, <<=, >>=, &=, |=, ^=
    Logical and, or, not

     

  3. Types Affect Semantics

    print 3 * 2
    print 3 * "abc"
    print 3 + 2
    print "abc" + "def"
    print 3 + "def"
     
  4. Operator Precedence
    print 2+3*4  # prints 14, not 20
    print 9**1/2 # prints 4, not 3
  5. Integer division
    print "20/3 =", (20/3)
    print " 6/3 =", ( 6/3)
    print " 5/3 =", ( 5/3)
    print " 2/3 =", ( 2/3)
    print " 0/3 =", ( 0/3)
    print "-2/3 =", (-2/3)
    print "-3/3 =", (-3/3)
    print "-4/3 =", (-4/3)
  6. The Modulus or Remainder operator (%)
    print "20%3 =", (20%3)
    print " 6%3 =", ( 6%3)
    print " 5%3 =", ( 5%3)
    print " 2%3 =", ( 2%3)
    print " 0%3 =", ( 0%3)
    print "-2%3 =", (-2%3)
    print "-3%3 =", (-3%3)
    print "-4%3 =", (-4%3)
    print " 3%0 =", ( 3%0)
  7. Integer vs Floating-point division
    print 10 / 3
    print 10.0 / 3
    print 10 / 3.0

    x = 10
    print x / 3
    print 1.0 * x / 3
    print float(x) / 3
  8. Approximate values of floating-point numbers
    d1 = 0.1 + 0.1 + 0.1
    d2 = 0.3
    print (d1 == d2) # False!
    print (d2 - d1) # -5.55111512313e-17 (tiny!)
  9. "almost equal" comparison of doubles
    d1 = 0.1 + 0.1 + 0.1
    d2 = 0.3
    print (d1 == d2) # still false, of course
    epsilon = 0.000001
    print (abs(d2 - d1) < epsilon) # True!
    Once again, using an almostEqual function (that we will write)
    
    def almostEqual(d1, d2):
        epsilon = 0.000001
        return (abs(d2 - d1) < epsilon)
    d1 = 0.1 + 0.1 + 0.1
    d2 = 0.3
    print (d1 == d2) # still false, of course
    print almostEqual(d1, d2) # True, and now packaged in a handy reusable function!
  10. Short-circuit evaluation
    x = 0
    y = 0
    print ((y != 0) and ((x/y) != 0)) # Works!
    print (((x/y) != 0) and (y != 0)) # Crashes!

    Once again, using the "or" operator

    x = 0
    y = 0
    print ((y == 0) or ((x/y) == 0)) # Works!
    print (((x/y) == 0) or (y == 0)) # Crashes!

    Yet another example:

    def isPositive(n):
        result = (n > 0)
        print "isPositive(",n,") =", result
        return result
    
    def isEven(n):
        result = (n % 2 == 0)
        print "isEven(",n,") =", result
        return result
    
    print "Test 1: isEven(-4) and isPositive(-4)"
    print (isEven(-4) and isPositive(-4)) # Calls both functions
    print "----------"
    print "Test 2: isEven(-3) and isPositive(-3)"
    print (isEven(-3) and isPositive(-3)) # Calls only one function!
  11. Truth Tables for Testing Logical Equivalence
    One of DeMorgan's Laws states:  A and B == not ((not A) or (not B))
     
    A B A and B not A not B (not A) or (not B) not ((not A) or (not B))
    0 0 0 1 1 1 0
    0 1 0 1 0 1 0
    1 0 0 0 1 1 0
    1 1 1 0 0 0 1

     

  12. Bitwise Operators
    # 0 1 1 0 = 4 + 2 = 6
    # 0 1 0 1 = 4 + 1 = 5
    
    print "6 & 5 =", (6&5)
    print "6 | 5 =", (6|5)
    print "6 ^ 5 =", (6^5)
    print "6 << 1 =", (6 << 1)
    print "6 << 2 =", (6 << 2)
    print "6 >> 1 =", (6 >> 1)
    print "~6     =", (~6)
    print "~6+1   =", (~6+1)   // what is going on (see 2's Complement below!)
    print "~432+1 =", (~432+1)
    
    print "Bitwise DeMorgan's Law:  a&b == ~(~a|~b)"
    print "6 & 5 =", (6&5)
    print "~(~6|~5) =", (~(~6|~5))
  13. Bit Masks
    def setLower4Bits(n):
        # SET bits with an OR mask of 1's
        mask = 0b00001111  # 0x0F (15), or just 0b1111, or just 0xF
        return n | mask
    
    print "setLower4Bits(0b11010110) =", bin(setLower4Bits(0b11010110))
    
    def clearLower4Bits(n):
        # CLEAR bits with an AND mask of 0's
        # Error: Due to leading zeroes, this clears all the bits above the 8th bit, too!
        mask = 0b11110000  # 0xF0 (240)
        return n & mask
    
    print "clearLower4Bits(0b11010110) =", bin(clearLower4Bits(0b11010110))
    print "clearLower4Bits(0b101011010110) =", bin(clearLower4Bits(0b101011010110))  // yikes!
    
    def clearLower4Bits(n):
        # CLEAR bits with an AND mask of 0's
        # Fix:  This way, we only clear the lowest 4 bits!
        mask = ~(0b1111)
        return n & mask
    
    print "clearLower4Bits(0b11010110) =", bin(clearLower4Bits(0b11010110))
    print "clearLower4Bits(0b101011010110) =", bin(clearLower4Bits(0b101011010110)) // whew!
    
    def getSecondLowestNybble(n):
        # A 'nybble' is half a 'byte', or 4 bits.
        # It is a value from 0b0000 (0) to 0b1111 (15).
        return ((n >> 4) & 0b1111) # or: ((n & 0b11110000) >> 4)
  14. Conditional Expressions
    # Note: you may not use conditional expressions until we cover
    # conditional ("if") statements in a week or two.
    # They are included here, though, as they are expressions, not statements,
    # so deserve some mention in the section on expressions.
    x = 2 y = 3 if (x < 4) else 5 z = 4 if (x > 4) else 5 print x, y, z
  15. Two's Complement
    1. Optional reading (Wikipedia)
       
    2. Why?
      1. Subtraction via complement and addition (saves circuit space, back when that mattered)
      2. No problem with "negative zero"  (1000)
        (but this means there is an "extra" negative number)
      3. It's a standard (the real reason now -- most real computers are really built this way today)
         
    3. With 3 Bits
      Bits Decimal Value
      000 0
      001 1
      010 2
      011 3
      100 -4
      101 -3
      110 -2
      111 -1
      • Leftmost (most significant) bit == "sign" bit (1 = negative, 0 = non-negative)
      • For non-negatives, value == |magnitude of non-sign bits|
      • For negatives, value == -(22) + |magnitude of non-sign bits|
      • Negation:  -x == ~x+1.
        • Check:
             011 = +3
             ~(011) = 100
             ~(011)+1 = 101
             101 == -3 (see table above).
          It works!
        • Check again, with code:
          def negate(x):
              return (~x + 1)
          
          print negate(1);
          print negate(-1);
          print negate(0);
          for x in xrange(12345):
              assert(negate(x) == -x);
              assert(negate(negate(x)) == x);
          print negate(12345678987654321);
    4. 10's Complement
      1. How to "bit flip" in base 10?  For digit d, take ~d to mean (9-d)
      2. So, let's negate 328:
             -328 = ~(328) + 1
                  = 671 + 1
                  = 672
      3. But what does that mean?
      4. To check, let's compute 404 - 328 by instead adding 405 + (-328) (modulo 1000)
             404 - 328 = 404 + (-328)
                       = (404 + 672) % 1000
                       = 1076 % 1000
                       = 76
        It works!
         
  16. Boolean Logic via Arithmetic
    1. The amazing power of arithmetic...
      # Boolean logic via arithmetic!
      # These functions demonstrate how you can use (ok, STRETCH) arithmetic
      # to compute seemingly boolean logical (ie, True/False) functions.
      # As is standard, we'll use 1 for True and 0 for False.
      # The functions operate over integer values.
      # These are not unique -- there are equivalent functions
      # that use other (possibly more clever) arithmetic approaches.
      # Still, these work, and provide a glimpse of the
      # remarkable computational power of good ol' arithmetic.
      
      def _not(x):             return 1-x
      def _and(x,y):           return x*y
      def _or(x,y):            return x+y-x*y
      def isNonZero(x):        return (x**2+2)%(x**2+1)
      def isZero(x):           return 1-((x**2+2)%(x**2+1))
      def isPositive(x):       return (1 +(2*x)/(2*x-1))/2
      def isNegative(x):       return (1 +(2*(-x))/(2*(-x)-1))/2
      def isGreaterThan(x, y): return (1 +(2*(x-y))/(2*(x-y)-1))/2
      def isLessThan(x, y):    return (1 +(2*(y-x))/(2*(y-x)-1))/2
      def isEqual(x, y):       return 1-(((x-y)**2+2)%((x-y)**2+1))
      
      def testBooleanLogicViaArithmetic():
          print "Testing 'boolean logic via arithmetic' functions... ",
          for x in xrange(-50,50):
              assert(isZero(x) == (x==0))
              assert(isNonZero(x) == (x != 0))
              assert(isPositive(x) == (x > 0))
              assert(isNegative(x) == (x < 0))
              assert(isGreaterThan(x,5) == (x > 5))
              assert(isGreaterThan(5,x) == (5 > x))
              assert(isLessThan(x,5) == (x < 5))
              assert(isLessThan(5,x) == (5 < x))
              assert(isEqual(x,5) == (x == 5))
          print "passed!"
      
      testBooleanLogicViaArithmetic()
    2. Once again, with some helper functions to make the logic clearer (in some cases)
      def _not(x): return 1-x
      
      def _and(x,y): return x*y
      
      def _or(x,y):
          # equivalent to: x+y-x*y
          return _not(_and(_not(x), _not(y))) # DeMorgan's Law
      
      def isNonZero(x): return (x**2+2)%(x**2+1)
      
      def isZero(x):
          # equivalent to: 1-((x**2+2)%(x**2+1))
          return _not(isNonZero(x))
      
      def isPositive(x): return (1 +(2*x)/(2*x-1))/2
      
      def isNegative(x):
          # equivalent to: (1 +(2*(-x))/(2*(-x)-1))/2
          # or:  _and(_not(isPositive(x), _not(isZero(x))))
          return isPositive(-x)
      
      def isGreaterThan(x, y):
          # equivalent to: (1 +(2*(x-y))/(2*(x-y)-1))/2
          return isPositive(x-y)
      
      def isLessThan(x, y):
          # equivalent to: (1 +(2*(y-x))/(2*(y-x)-1))/2
          return isGreaterThan(y,x)
      
      def isEqual(x, y):
          # equivalent to: 1-(((x-y)**2+2)%((x-y)**2+1))
          return isZero(x-y)
    3. And now to show off a little... :-)
      # This is a function from the notes on loops (coming soon!),
      # rewritten here using the techniques above,
      # and with the variables anonymized to make the
      # outcome a welcome surprise.  Run the code below
      # to see what it does.  All without booleans or (mostly) conditionals.  Amazing!
      def np(n):
          n += 1
          g = 0
          while (n):
              g += 1
              c = 0
              f = g
              while (f):
                  c += 1-(((g%f)**2+2)%((g%f)**2+1))
                  f -= 1
              n -= (((1 +(2*(g-1))/(2*(g-1)-1))/2) *
                    (1-(((c-2)**2+2)%((c-2)**2+1))))
          return g
      
      for n in range(10):
          print n, np(n)
  17. Some Practice
    # Enter these expressions and statements into the interpreter.
    # Predict the results of each line before advancing to the next line.
    # Be precise.
    
    print 3*3/4, 3/4*3, 3**3/4
    
    x = 1000*1000*1000*1000 # one trillion
    x
    print x
    type(x)
    x /= 1000*1000*1000 # divide by one billion 
    x
    x/1000
    x
    
    x = 123
    x.bit_length()
    
    import sys
    x = sys.maxint  # 2^31-1, or 2147483647
    x.bit_length()
    x
    x+1
    -x
    -(x+1)
    -x-1
    -x-2
    
    not 43
    not 43/99
    43/99 or 99/43 or 99
    
    print 0xff
    print hex(255)
    print 255 == 0xff
    print 255 == hex(255)
    
    print "-----------------"
    x = 5
    print 42 if (x == 5) else 99
    print ((x == 5) and 42) or 99
    print ((x == 5) * 42) + ((x != 5) * 99)
    print 42 + (x/6)*(99-42)
    print 42 + ((x-5)*(99-42))
    
    print "-----------------"
    x = 6
    print 42 if (x == 5) else 99
    print ((x == 5) and 42) or 99
    print ((x == 5) * 42) + ((x != 5) * 99)
    print 42 + (x/6)*(99-42)
    print 42 + ((x-5)*(99-42))

carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem