Search code examples
rubyradix

Why does the FixNum#to_s method in in Ruby only accept radixes from 2 to 36?


I looked at the documentation and even peeked at the C source, and I can't see why they limited the accepted radixes to 2..36. Anybody know?


Solution

  • As others have pointed out, radix < 2 is troublesome to render. and there's no conventional agreement on what characters to use for radixes larger than ['0'..'9'] + ['a'..'z'], which is why the standard method doesn't support radix outside those limits.

    If you really want a custom radix representation, you would need to define the alphabet of symbols to use for the digits. Here's a little module that will give you the capability.

    module CustomRadix
      # generate string representation of integer, using digits from custom alphabet
      # [val] a value which can be cast to integer
      # [digits] a string or array of strings representing the custom digits
      def self.custom_radix val, digits
    
        digits = digits.to_a unless digits.respond_to? :[]
        radix = digits.length
        raise ArgumentError, "radix must have at least two digits" if radix < 2
    
        i = val.to_i
        out = []
        begin
          rem = i % radix
          i /= radix
          out << digits[rem..rem]
        end until i == 0
    
        out.reverse.join
      end
    
      # can be used as mixin, eg class Integer; include CustomRadix; end
      # 32.custom_radix('abcd') => "caa" (200 base 4) equiv to 32.to_s(4).tr('0123','abcd')
      def custom_radix digits
        CustomRadix.custom_radix self, digits
      end
    end
    

    example use:

    $ irb
    >> require '~/custom_radix'
    => true
    >> CustomRadix.custom_radix(12345,'0'..'9')
    => "12345"
    >> CustomRadix.custom_radix(12345,'.-')
    => "--......---..-"
    >> funny_hex_digits = ('0'..'9').to_a + ('u'..'z').to_a
    => ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "u", "v", "w", "x", "y", "z"]
    >> CustomRadix.custom_radix(255, funny_hex_digits)
    => "zz"
    >> class Integer; include CustomRadix; end
    => Integer
    >> (2**63).custom_radix(funny_hex_digits)
    => "8000000000000000"
    >> (2**64+2**63+2**62).custom_radix(funny_hex_digits)
    => "1w000000000000000"
    >> base64_digits = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a << '+' << '/'
    => ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/"]
    >> 123456.custom_radix(base64_digits)
    => "eJA"