randy newfield randy newfield - 5 months ago 8
Ruby Question

Is there a way to force ruby to output results in binary

I want to do a demo for some peers, and I want to force IRB to return values in only binary.

For example it currently returns the result in base 10 no matter which base the input is in:

0b1111
# => 15 #should return 1111 or 0b1111
0b0001 | 0b0011
# => 3 #should return 0011 or 0b0011


Is there a way to force this result? I want to demo bitwise operators and it is much easier for them to understand if they see the bits flowing around rather than base 10 numbers being returned, that I would have to convert to base 2 afterwards.

Also I would like for all results to be in multiples of 4 bits. If possible with underscores or spaces separating the half byte groupings.

For example:

0b0001_0101
# => 0b0001_0101 #or 0b0001 0101, or 0001 0101, or 0001_0101


If I result does not need to be represented by 4 bits (example 3, 11) pad it to 4, 8, 16 bits in length depending on the number.

Answer

If you write 0b0001.class you will find that it is Fixnum.

Writing puts 0b1000 shows 8, and it's clear this Fixnum is stored in base 10.

So as far as I'm aware, there isn't any way to prevent the conversion to base 10.

If you want to control the way that Fixnum objects are displayed in IRB, you can implement a custom inspect method for the class:

class Fixnum
  def inspect
    unpadded_binary_string = to_s(2)
    binary_width = if unpadded_binary_string.length % 4 == 0
      unpadded_binary_string.length
    else
      ((unpadded_binary_string.length / 4) * 4) + 4
    end
    padded_binary_string = "%0#{binary_width}d" % unpadded_binary_string
    # join groups of 4 with an underscore
    padded_binary_string = padded_binary_string.scan(/.{4}/).join("_")
    "0b" + padded_binary_string
  end
end

results:

irb(main):007:0> 0b1000
=> 0b1000
irb(main):011:0> 99999999
=> 0b0101_1111_0101_1110_0000_1111_1111

The inspect method uses to_s(2), which takes an integer and produces a string representation of it's binary. But the zeroes at the front of the binary are lost when it's converted to base 10. That's why the inspect method needs to manually add zeroes to the front of the string.

There's no way I can think of to add the correct number of zeroes to the front of the string in a completely dynamic way.

What I'm doing here is calculating the minimum width (in a multiple of 4) that can contain the unpadded binary string. So if the unpadded length is 5 characters, the final width will be 8. If the unpadded length is 2, the final length is 4.

Instead of calculating it on-the-go, you could alternatively set the binary_width as an external variable that you change at runtime, then reference it from the inspect function.