【问题标题】:Recursive function to convert between number bases fails at certain numbers在数字基数之间转换的递归函数在某些数字处失败
【发布时间】:2014-09-15 00:03:24
【问题描述】:

我正在尝试创建一种算法,可以将基数为 10 的数字转换为基数为 n 的数字,其中 n 最多为 10。但是,由于某些奇怪的原因,C 中的以下算法在每个基数的某些关键点处失败。例如,对于以 2 为底和以 3 为底的转换,包括 1023 和 52,487 在内的所有数字分别有效,但超出此范围的数字会产生一些奇怪的负面结果。我不知道为什么会这样。谁能帮帮我?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int returnint;

int baseconvert(int number,int base) {
    if(number == 0 || base == 10) {
        return returnint;
    }
    returnint = (number % base) + (10 * baseconvert(number / base, base));
    return returnint;
}

int main() {
    fprintf(stdout,"%d\n",baseconvert(1023,2));
    fprintf(stdout,"%d\n",baseconvert(52487,3));
}

编辑:

以下是上述打印语句的打印结果,如果有帮助的话:

1410065408
-2094967296

【问题讨论】:

  • baseconvert(1023,2) 确实在 gcc 中工作,baseconvert(52487,3) 很可能是溢出。
  • 不是答案,只是一个注释:您在main() 函数中缺少return
  • @dari 是的 baseconvert(1023,2)baseconvert(52487,3) 工作;这些是作为最高值的示例发布的。正如我在下面对 Mark Wilkin 的帖子所评论的那样,我尝试使用更大的整数,但这不起作用。
  • 这里的主要问题是您将整数视为专用于特定基数。整数也用于表示扩展到负范围的事物的计数。您要在int 变量中保存的值是计数,例如,一袋苹果的数量。不管你如何在不同的基础上表示它,那个袋子里都有固定数量的苹果,这个固定数量的苹果要存放在int 中。基数仅在输出值时才重要。例如:int n = 20; printf( "%d %x %o", n, n, n ); 会将n 输出为201424
  • 代码有错误。如果基数为 10,则代码返回 'returnint' 但是,var returnint 尚未设置,因此该函数将返回垃圾。

标签: c base base-conversion


【解决方案1】:

您的算法在数字与基数的范围内非常有限。基数越小,表示它所需的数字就越多。而且由于您以十进制形式存储结果,您将很快浪费可用的数据范围。没有基本数据类型可以保存所有可能输入的结果。例如,最大 31 位十进制数(普通整数,去掉符号位)将导致 31 位输出!

您有多种选择来解决这个问题:

  • 分配一个足够大的堆栈并将数字压入其中。完成后,打印堆栈内容。
  • 立即打印数字而不保存,这样就无需分配任何东西。 例如:

#include <stdio.h>

void baseconvert(int number,int base) 
{
    if(number > 0) 
    {
        int digit = (number % base);
        baseconvert(number / base, base);
        printf("%d",digit);
    }
    else
    {
        printf("\n");
    }
}

int main() 
{
    baseconvert(1023,2);
    baseconvert(52487,3);
}

【讨论】:

    【解决方案2】:

    整数值似乎溢出了。例如,以 2 为底的十进制值 1023 是 1111111111。如果它是 4 字节整数,那么在尝试“添加”一位数字时它会溢出(本例中的最大有符号整数为 2147483647)。

    由于您的目标似乎是以不同的基数显示数字,因此将数字存储在字符数组中可能更有意义。

    【讨论】:

    • 嗯,即使我将 returnint 的大小增加到 unsigned long int (并按照 lgor 的建议将其放在函数 baseconvert 的主体中),即使整数现在可以是 32,该值仍然会溢出位大小。也许如果我改用将数字附加到字符串......我会马上回来。
    • 取决于平台,long 可能仍然只有 4 个字节而不是 8 个字节,因此这可能无济于事。而且我看到我在您建议的同时建议了字符数组。 :)
    【解决方案3】:

    在我所知道的所有处理器上,整数已经以二进制形式存储;这意味着基数 2。这个值可以显示在您想要的任何基数中,您需要做一些工作。 printf() 和朋友允许您轻松地以基数 10 (%d) 和基数 16 (%x) 打印。不难想象有一种方法可以将二进制(以 2 为基数)整数值转换为以 n 为基数的字符表示。

    我严重怀疑您是否真的打算像您所做的那样更改整数的实际值。就像上面的@ThoAppelsin 所说,袋子里的苹果数量保持不变,不管你选择哪个底座来展示。

    通过简单地创建一个表示(用数字)任何基数的整数的方法,您还将解决溢出问题!

    【讨论】:

      【解决方案4】:

      您的结果超出了整数范围。尝试改用字符串。这是依赖字符串来表示数字的伪代码,它可以将数字从任何基数转换为 2 到 36 之间的任何其他基数(使用数字和大写字母):

      function ConvertNumber(number, b, d)
      begin
          newNumber = ""
          while number <> "0"
              begin
                  number = Divide(number, b, d, out remainder)
                  newDigit = ValueToDigit(remainder)
                  newNumber = Concatenate(newDigit, newNumber)
              end
          if newNumber ="" then
              newNumber = "0"
      end
      
      function Divide(number, base, divisor, out remainder)
      begin
          remainder = 0
          result = ""
          for i = 0 to Length(number) - 1
              begin
                  digitValue = DigitToValue(number[i])
                  remainder = base * remainder + digitValue
                  newDigitValue = remainder / divisor -- integer division
                  remainder = remainder mod divisor
                  if newDigitValue > 0 OR result <> "" then
                      newDigit = ValueToDigit(newDigitValue)
                      result = Concatenate(result, newDigit)
              end
          if result = "" then
              result = "0"
          return result
      end
      

      您可以在这篇文章中找到完整的数学和实现:Converting Number Bases

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-27
        • 2015-07-14
        • 2010-11-23
        • 1970-01-01
        相关资源
        最近更新 更多