整数在较高值下表现出奇怪的行为

本文关键字:高值下 整数 | 更新日期: 2023-09-27 18:30:50

我在 C# 中创建了一个程序,用于将一个数字从一个基数转换为任何其他基数(例如,Base-10 中的 10 = Base-11 中的 A)。

对于每个目标基数,似乎存在程序导致不准确值的某个点。 例如,当从 Base-10 转换为 Base-11 然后转换回 Base-10 时,错误计算(导致比我开始的少一个整数)发生在等于或大于 45949729863572161 的值处(巧合地相当于 11 16;谁会想到?)。

为了进行转换,我首先将一个数字转换为 Base-10,然后转换为目标基数。我没有真正的猜测是导致此错误的原因

最有可能的罪魁祸首是主要方法。
A) 转换为基数 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) 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;
}

其他潜在方法

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

如果上述方法不是问题,我的转换代码的更广泛版本可以作为 Github Gist 使用。

关于问题是什么的任何想法?我怀疑它是否达到了乌龙的最大值,尽管除此之外我不确定。

感谢您的任何帮助!

整数在较高值下表现出奇怪的行为

Math.Pow 以双打进行算术运算。双精度只有大约 16 位十进制数字;当数字在 10 到 16 的量级上时,您将开始累积表示错误。你的是。

请改用BigInteger.Pow()。它具有任意大的精度。

顺便说一句,你不是第一个发现将 11 的 16 次方放入双精度的事实的人:

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

我尝试使用将45949729863572161从基数 10 转换为基数 11 的测试用例在 LINQPad (v4) 中运行您的代码,我得到了正确答案 (10000000000000000000),我使用 Wolfram Alpha 进行了验证。

我还尝试进行相同的转换,将一个添加到您的值中(即 45949729863572162),但我仍然得到了正确的答案 (10000000000000001)。

所以,我认为你的问题的答案是,要么行为不像你想象的那么奇怪,要么你使用的是有错误的旧版本的编译器。


回复您的评论,问题似乎与Math.Pow功能有关。

Math.Pow(11, 16)正在产生45949729863572160而不是45949729863572161。当我输入这个时,我看到主人(Eric Lippert)已经回答了,所以我就让他解释为什么以及如何解决这个问题:)