2

I have created a program, in C#, to convert a number from one base to any other base (e.g. 10 in Base-10 = A in Base-11).

For each target base, there seems to be a certain point where the program results in an inaccurate value. E.g., when converting from Base-10 to Base-11 then back to Base-10, the miscalculation (which results in a integer one less than what I started) occurs at any value equal to or greater than 45949729863572161 (which is coincidentally equivalent to 1116; who woulda' thought?).

To make my conversions, I first convert a number to Base-10 then to the target base. I have no real guess as to what point is the cause of this error

The most likely culprits are the primary methods.
A) The method to convert to Base-10

static string toBase10(string origVal, uint origBase)
{
    ulong result = 0;
    //Convert each character to base-ten so that base-ten arithmetic can be used
    ulong[] b10Array = charsToBase10Readable(origVal, findSubArray(origBase));

    //Run through each place value to convert entire number to base-ten
    for (uint n = 0; n < b10Array.Length; n++)
    {
        ulong val = b10Array[n];
        result += val * ((ulong)Math.Pow(origBase, b10Array.Length - n - 1));
    }

        return result.ToString();
    }
}

B) The method to convert from Base-10

static string fromBase10(ulong num, uint targetBase)
{
    string result = string.Empty;
    //Generate the original base
    char[] targetBaseChars = findSubArray(targetBase);
    do
    {
        result = targetBaseChars[num % (ulong)targetBase] + result;
        num /= (ulong)targetBase;
    }
    while (num > 0);

    return result;
}

Other potential methods

static char[] findSubArray(uint i)
{
    char[] subArray = new char[i];
    for (uint n = 0; n < i; n++)
    {
        subArray[n] = masterBase[n];
    }
    return subArray;
}

In case the above methods aren't the issue, a more expansive version of my conversion code is available as a Github Gist.

Any thoughts as to what the issue is? I doubt that it is hitting ulong's max, although beyond that I am unsure.

Thank you for any assistance!

Paul
  • 25
  • 1
  • 6
  • Can you provide the code for `findSubArray` ? Thanks. – minghan Jan 18 '16 at 05:01
  • @minghan Added; to note, all of the code is available in context in the linked Github Gist. Would it be better to move all of the content here? – Paul Jan 18 '16 at 05:06
  • I ran your code in VS using `fromBase10(45949729863572161L, 11))` and `baseToBase("45949729863572161", 10, 11))`. The results are correct. I don't see an issue with your code. – minghan Jan 18 '16 at 05:43

2 Answers2

7

Math.Pow does its arithmetic in doubles. Doubles only have about 16 decimal digits of precision; you'll start getting representation errors accumulating when the numbers are on the order of 10 to the 16. Which yours are.

Use BigInteger.Pow() instead. It has arbitrarily large precision.

Incidentally, you are not the first person to discover this fact about putting 11 to the 16th power into a double:

https://math.stackexchange.com/questions/91583/implementing-fermats-primality-test/91584#91584

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
2

I tried running your code in LINQPad (v4) using your test case of converting 45949729863572161 from base 10 to base 11, and I got the correct answer (10000000000000000), which I verified using Wolfram Alpha.

I also tried doing the same conversion with one added to your value (i.e., 45949729863572162), and I still got the correct answer (10000000000000001).

So, I think the answer to your question is that either the behavior is not as odd as you think it is, or you're using an old version of the compiler that has a bug.


Responding to your comment, it appears that the problem is relating to the Math.Pow function.

Math.Pow(11, 16) is yielding 45949729863572160 instead of 45949729863572161. As I'm typing this, I see the master (Eric Lippert) has answered, so I'll just let him explain why and how to work around it :)

devuxer
  • 41,681
  • 47
  • 180
  • 292
  • Thanks for your response; I found that converting to Base-11 does indeed work, although by backtracking my steps, I found that the issue arises when converting back to Base-10. 10000000000000000 -> 45949729863572160, which is one less than what I started with. Any ideas? – Paul Jan 18 '16 at 06:02