views:

374

answers:

3

Does .NET have a native function that is equivalent to PHP's base_convert or will I need to write my own? I want to convert from any base to any other base -- where either the 'to' base or the 'from' base can be any integer 2-36.

Example of the PHP function: base_convert($number_to_convert, $from_base, $to_base)

// convert 101 from binary to decimal
echo base_convert('101', 2, 10);
// 5

As noted by Luke in the comments of Jon Skeet's answer: Convert.ToString can't handle conversion to/from any arbitrary base, only 2, 8, 10 and 16

Update: Apparently, the answer is: no, there is no native way. Below, Erik shows one way to do this. Another implementation is here: http://www.codeproject.com/KB/macros/Convert.aspx

+10  A: 

EDIT: This answer is very convenient, but only works for bases 2, 8, 10 and 16

You can use Convert.ToInt32(text, base) and then Convert.ToString(number, base):

using System;

class Test
{
    static void Main()
    {
        int number = Convert.ToInt32("101", 2);
        string text = Convert.ToString(number, 10);
        Console.WriteLine(text); // Prints 5
    }
}

If you're converting to or from base 10, you don't need to specify that - it's the default.

Note that this only works for bases 2, 8, 10 and 16. If you want anything else, you'll have to write your own parser/formatter.

Jon Skeet
6 whole seconds.
Jeff Yates
Note that Convert.ToInt32 and Convert.ToString can't handle conversion to/from any arbitrary base, only 2, 8, 10 and 16.
LukeH
@Luke: you just beat me to it
Dinah
@Jeff - if it took Jon only 6 seconds to post this response, then he typed the original response at a rate of ~9 words per second, or ~540 words per minute. @Jon - I want your keyboard.
Erik Forbes
@Erik: No, he just beat me by 6 seconds to his initial response, which was not yet as complete as it stands now. I have now deleted my lesser and slower response, carrying over any portion of value to this one. He wrote the first draft in 20 seconds or so whereas mine took 26. Sigh.
Jeff Yates
Oh I see. Okay - ~2.7 words per second, or ~160 wpm. Human after all. .. ..... ... Or... Is he?
Erik Forbes
You can tell that a certain member has achieved super-human status when you feel that by down-voting them, you did something wrong
Dinah
@Dinah: So what bases do you need? You say "arbitrary" - how do you represent things in base 100? Please edit the question to elaborate on your requirements.
Jon Skeet
@Luke: I knew it was at best "up to 16" - I tried to check the docs for whether it could cope with anything up to 16, but they didn't show properly on Android :( (I was off the train by then...)
Jon Skeet
Hell, you did this on a train? When will it end!? =P
Erik Forbes
I do a lot of SO on the train. Netbooks rock. I haven't posted many answers from my phone, but it does happen...
Jon Skeet
Lucky - my commute is hands-on the whole way. No bus stops anywhere near where I live. :( All my SO-ing happens at work.
Erik Forbes
@Jon Skeet: Updated. arbitrary = 2 through 32
Dinah
@Dinah: Thanks. I'll edit this answer to make it more obvious that it doesn't address the question. No need to fix it given that you've already got a perfectly good answer :)
Jon Skeet
The BCL implementation is probably more efficient than mine, as well.
Erik Forbes
+5  A: 

Here's some code that'll convert an integer to an arbitrary base up to 36, and convert a string representation of a base x value to an integer (given the base):

class Program {
 static void Main(string[] args) {
  int b10 = 123;
  int targetBase = 5;

  string converted = ConvertToBase(b10, targetBase);
  int convertedBack = ConvertFromBase(converted, targetBase);

  string base3 = "212210";
  string base7 = ConvertFromBaseToBase(base3, 3, 7);

  Console.WriteLine(converted);
  Console.WriteLine(convertedBack);
  Console.WriteLine(base7);
  Console.ReadLine();
 }

 private const string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

 private static string ConvertToBase(int b10, int targetBase) {
  if (targetBase < 2) throw new ArgumentException("Target base must be greater than 2.", "targetBase");
  if (targetBase > 36) throw new ArgumentException("Target base must be less than 36.", "targetBase");

  if (targetBase == 10) return b10.ToString();

  StringBuilder result = new StringBuilder();

  while (b10 >= targetBase) {
   int mod = b10 % targetBase;
   result.Append(chars[mod]);
   b10 = b10 / targetBase;
  }

  result.Append(chars[b10]);

  return Reverse(result.ToString());
 }

 private static int ConvertFromBase(string bx, int fromBase) {
  if (fromBase < 2) throw new ArgumentException("Base must be greater than 2.", "fromBase");
  if (fromBase > 36) throw new ArgumentException("Base must be less than 36.", "fromBase");

  if (fromBase == 10) return int.Parse(bx);

  bx = Reverse(bx);
  int acc = 0;

  for (int i = 0; i < bx.Length; i++) {
   int charValue = chars.IndexOf(bx[i]);
   acc += (int)Math.Pow(fromBase, i) * charValue;
  }

  return acc;
 }

 public static string ConvertFromBaseToBase(string bx, int fromBase, int toBase) {
  int b10 = ConvertFromBase(bx, fromBase);
  return ConvertToBase(b10, toBase);
 }

 public static string Reverse(string s) {
  char[] charArray = new char[s.Length];
  int len = s.Length - 1;
  for (int i = 0; i <= len; i++)
   charArray[i] = s[len - i];
  return new string(charArray);
 }
}

If you're unconcerned with displaying these values, you can use extended characters in your chars set - if you stick to plain ascii, you can theoretically have base256 values. Going beyond that I would recommend not using chars, but instead using some other uniquely-identifiable value - though I don't much see the value.

Erik Forbes
Nice but this doesn't allow for an arbitrary starting base. It assumes a starting base of 10
Dinah
Okay, updated to include a method that will convert from an arbitrary base to an arbitrary base...
Erik Forbes
Very nice. Accepted!
Dinah
Nice solution. I was about to mention elsewhere about how arbitrary bases is difficult beyond 36 because of running out of symbols to represent the digits. +1
Jeff Yates
Funny - I just addressed that in an edit. =P
Erik Forbes
@Erik: Aww, you should have supported base 1 (tally) ;-]
Daniel LeCheminant
Heh, trying to use base 1 would result in an infinite loop. =)
Erik Forbes
Base 1 integers would be really easy. To base 1: print that qty of 1s. From base 1: count the number of 1s.
Dinah
I don't know why I didn't catch it before but there's something wrong with your program. Your example is 123321 as a base 3 number. A base 3 number can't include 3s. I tried ConvertFromBaseToBase("123321", 7, 3) and it yields 311021110. Again, it shouldn't contain 3s
Dinah
Yeah, I know - there's some more validation to perform, but the gist of the code is there. For converting from a non-10 base, you'll need to validate that all the characters in the input set are valid in the specified base. Shall we say, left as an exercise to the reader?
Erik Forbes
+1  A: 

In ConvertToBase, the following line:

while (b10 > targetBase)

...should be:

while (b10 >= targetBase)

Which takes care of the base number popping up in the converted number (e.g. converting "3" into base 3 yields "3" instead of "10").

Wongo