# Ruby of course supports your run-of-the-mill bitwise stuff # In case you've never come across this stuff before, here's a # quick intro. # # NOTE: This is theory and doesn't take account of realities like # endianess, signedness and so forth. I'll just assume LSB # ('least significant bit' in terms of the full number) == bit 0, # and will also consider Fixnum as 8-bit for the purposes of this. # # NOTE ALSO: You probably won't need to use this stuff in Ruby all # that much, but it is right at the heart of programming so if # you're a 'got to know' type like me, you'll want to read it. # Otherwise, probably best to forget all about it because it could # confuse matters. # Imagine we have some bitmaps: # 1 2 4 8 16 32 64 128 # 1 0 0 1 0 1 0 0 = 41 # 1 0 0 0 0 0 0 1 = 129 # 0 0 0 0 0 1 0 0 = 32 # 1 1 1 1 1 1 1 1 = 255 # # And we wonder about bit 5 (v=32). Well, we can easily find out # using bitwise and (&), comparing the mask for the bit we need # to check. bmps = [41, 129, 8] bmps.each do |it| if it & 32 > 0 puts "#{it} - Bit 5 is set" else puts "#{it} - Bit 5 isn't set" end end # It should be obvious why this works - 'and' returns a number with # any bits *that were set in both original numbers* set. # The following illustrates: # # 1 2 4 8 16 32 64 128 # 0 1 0 1 0 0 0 0 = 9 # 0 0 0 1 0 0 0 0 = 8 # AND 0 0 0 1 0 0 0 0 = 8 # # So to figure out if bit 'n' is set, we just need to figure out # the mask for that bit. Did you notice they're all powers of # two? Well, that's no coincidence... class Fixnum def bit_set(n) self & (2 ** n) > 0 # or, self & (2**n) == (2**n) end # handy for examples def show_bits print "#{self.to_s}:\t" (0..7).each { |n| print "#{self.bit_set(n) ? 1 : 0}\t" } print "\n" end end bmps.each do |bmp| bmp.inspect bmp.show_bits end print "\n" # Okay. So now we can check bitmaps we're passed in. But what about # making up out own bit-sets? Lets' look at 'or', which as you might # expect, returns a number with any bits *that were set in either # original number* set: # # 1 2 4 8 16 32 64 128 # 0 1 0 1 0 0 0 0 = 10 # 0 0 0 1 0 0 1 0 = 72 # OR 0 1 0 1 0 0 1 0 = 74 # # Notice the subtlety? 'or' actually means 'set in either or both'. # We can use this - if we supply a bitmask and an original number, # then we'll get back a number with all the original bits set, # as well as the mask ones. Let's set bit 4 (v == 16) in 72. # 72.show_bits 16.show_bits puts "OR" (72 | 16).show_bits # becomes 88 # Okay, so that's all good. But what about switching them off? # For this we're best to go to a relative or 'or' - 'xor'. # As the name utterly fails to suggest, this is 'Exclusive' or, # i.e. one or the other, but not both. puts "XOR" (88 ^ 16).show_bits # becomes 72 again # There's also a smooth effect here - XOR doesn't actually # 'switch bits off', but instead it toggles them to their # opposite state. (72 ^ 16).show_bits # becomes 88 again # So the obvious way to control bits is: class Fixnum def flip_bit(n) self ^ (2**n) end def set_bit(n,t) bit_set(n) == t ? self : flip_bit(n) end end # We can now do flipping and setting willy-nilly. print "\n\n=================================================================\n" print "72.flip_bit 4 = " 72.flip_bit(4).show_bits print "88.flip_bit 4 = " 88.flip_bit(4).show_bits print "72.set_bit 4, true = " 72.set_bit(4,true).show_bits print "72.set_bit 4, false= " 72.set_bit(4,false).show_bits print "88.set_bit 4, true = " 88.set_bit(4,true).show_bits print "88.set_bit 4, false= " 88.set_bit(4,false).show_bits print "\n\n" # For completeness, we should also take a look at bitwise negation, with # the unary ~ operator. This is pretty self-evident I think. # # NOTE: The fact that Fixnum is signed slightly messes this up, but the # effect on the bits is still the same - ignore the actual number, the # demo is in the bit table ;) 72.show_bits (~72).show_bits print "\n\n" # ========================================================================= # This is only the very basics - you're encouraged to go read up on this # stuff, because understanding the concepts on which computers are built # will help you be a better programmer. We now return to you'r regularly # scheduled Ruby-specific wonderment. # Just one more thing to say really, which is that Ruby supports shortcut # assignment operators for bitwise operations: a = 72 a |= 16 puts "OR: #{a}" a = 88 a ^= 16 puts "XOR: #{a}" a = 88 a &= 16 puts "AND: #{a}" # Obviously, negation isn't supported in this context since it is a # unary operator anyway. It's important not to confuse these with the # 'improvement' operators, (see oper.rb) which are actually conditional # assignment operators and do something very different.