Converting a number to an arbitrary Radix


One of the things that is great about integers and longs is that they are easy to remember. However, when using this as identifiers that a human should be able to type in, they leave a lot to be desired. A integer in the billion range requires a user to enter 10 digits correctly. That’s hard to read, hard to keep your place, etc. There is a solution to this issue: represent the data using a radix other than 10. For English speakers, a radix of 36 is easily readable and maximizes the density while allowing for case insensitivity (no one wants to remember if they should type z or Z!).

Consider this, the value for int.MaxValue is written out as:

2147483647

As base 36, it is

zik0zj

6 characters instead of 10. Nice!

To do this, I wrote a simple function that converts a long (64 bits!) to any radix between 2 and 36. This is a basic first or second semester CS problem, I know. Still, this code is handy to have when you need it for converting numbers into something a person can type in:

 

static string ConvertToString(long value, int toBase)
{
  if (toBase < 2 || toBase > 36)
  {
    throw new ArgumentOutOfRangeException("toBase",
      "Must be in the range of [2..36]");
  }
  var values = new List<char>();
  for (var val = '0'; val <= '9'; ++val)
  {
    values.Add(val);
  }
  for (var val = 'a'; val <= 'z'; ++val)
  {
    values.Add(val);
  }

  var builder = new StringBuilder();
  bool isNegative = false;
  if (value < 0)
  {
    value = -value;
    isNegative = true;
  }
  do
  {
    long index = value%toBase;
    builder.Insert(0, values[(int)index]);
    value = value/toBase;
  } while (value != 0);
  if (isNegative)
  {
    builder.Insert(0, '-');
  }
  return builder.Length == 0 ? "0" : builder.ToString();
}

And, to go the other way:

 

static long ConvertToLong(string input, int fromBase)
{
  if (fromBase < 2 || fromBase > 36)
  {
    throw new ArgumentOutOfRangeException("fromBase",
      "Must be in the range of [2..36]");
  }
  if (string.IsNullOrEmpty(input)) return 0;
  input = input.Trim();
  var values = new List<char>();
  for (var val = '0'; val <= '9'; ++val)
  {
    values.Add(val);
  }
  for (var val = 'a'; val <= 'z'; ++val)
  {
    values.Add(val);
  }

  var builder = new StringBuilder();
  bool isNegative = false;
  int startIndex = 0;
  if (input[0] == '-')
  {
    isNegative = true;
    ++startIndex;
  }
  long retval = 0;
  for(int index = startIndex; index < input.Length; ++index)
  {
    retval *= fromBase;
    bool found = false;
    for (int number = 0; number < fromBase; ++number)
    {
      if (input[index] == values[number])
      {
        retval += number;
        found = true;
      }
    }
    if (!found) break;
  }
  if (isNegative)
  {
    retval = -retval;
  }
  return retval;
}

%d bloggers like this: