【问题标题】:How to convert an arbitrary large integer from base 10 to base 16?如何将任意大整数从基数 10 转换为基数 16?
【发布时间】:2010-10-25 19:37:45
【问题描述】:

程序需要一个任意大的无符号整数的输入,该整数表示为以 10 为底的一个字符串。输出是另一个以 16 为底表示整数的字符串。

例如输入为“1234567890987654321234567890987654321234567890987654321”, 并且输出应为“CE3B5A137DD015278E09864703E4FF9952FF6B62C1CB1”

算法越快越好。

如果输入限制在 32 位或 64 位整数之内,那就很容易了;例如,以下代码可以进行转换:

#define MAX_BUFFER 16
char hex[] = "0123456789ABCDEF";

char* dec2hex(unsigned input) {
    char buff[MAX_BUFFER];
    int i = 0, j = 0;
    char* output;

    if (input == 0) {
        buff[0] = hex[0];
        i = 1;
    } else {
        while (input) {
            buff[i++] = hex[input % 16];
            input = input / 16;
        }
    }

    output = malloc((i + 1) * sizeof(char));
    if (!output) 
        return NULL;

    while (i > 0) {
        output[j++] = buff[--i];        
    }
    output[j] = '\0';

    return output;
}

真正具有挑战性的部分是“任意大”无符号整数。我用谷歌搜索过,但他们中的大多数都在谈论 32 位或 64 位内的转换。没有找到运气。

谁能给出任何点击或任何可以阅读的链接?

提前致谢。

编辑这是我最近遇到的一个面试问题。谁能简要解释一下如何解决这个问题?我知道有一个 gmp 库并且我以前使用过它;然而,作为一个面试问题,它不需要使用外部库。

【问题讨论】:

  • @S.Lott - 这也是我的直接想法
  • 不……这不是家庭作业;一个面试问题。 :P

标签: c base


【解决方案1】:

这是一个 BigInt 库:

http://www.codeproject.com/KB/cs/BigInt.aspx?msg=3038072#xx3038072xx

不知道它是否有效,但这是我在 Google 上找到的第一个。它似乎具有解析和格式化大整数的功能,因此它们也可能支持不同的基数。

编辑:啊,你使用的是 C,我的错。但是您可能能够从代码中获得灵感,或者使用 .NET 的人可能会有同样的问题,所以我将把它留在这里。

【讨论】:

    【解决方案2】:

    真正具有挑战性的部分是“任意大”无符号整数。

    您是否尝试过使用GNU MP Bignum 库?

    【讨论】:

    • 是的,我知道 gmp 并且以前使用过它;不使用gmp可以解决这个问题吗?或者,由于代码库很大,您能简要介绍一下gmp是如何设计的
    【解决方案3】:

    Unix dc 能够对任意大整数进行基本转换。开放 BSD 源代码可在here 获得。

    【讨论】:

      【解决方案4】:
      1. 分配一个整数数组,元素个数等于输入字符串的长度。将数组初始化为全0。

        这个整数数组将以 16 为基数存储值。

      2. 将输入字符串中的十进制数字添加到数组的末尾。将现有值乘以 10 添加结转,将新值存储在数组中,新结转值为 newvalue div 16。

        carryover = digit;
        for (i = (nElements-1); i >= 0; i--)
        {
            newVal = array[index] * 10) + carryover;
            array[index] = newval % 16;
            carryover = newval / 16;
        }
        
      3. 打印数组,从第 0 个条目开始并跳过前导 0。


      这里有一些可以工作的代码。毫无疑问,可能会进行一些优化。但这应该足以作为一个快速而肮脏的解决方案:

      #include <stdio.h>
      #include <string.h>
      #include <stdlib.h>
      #include "sys/types.h"
      
      char HexChar [16] = { '0', '1', '2', '3', '4', '5', '6', '7',
                            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
      
      static int * initHexArray (char * pDecStr, int * pnElements);
      
      static void addDecValue (int * pMyArray, int nElements, int value);
      static void printHexArray (int * pHexArray, int nElements);
      
      static void
      addDecValue (int * pHexArray, int nElements, int value)
      {
          int carryover = value;
          int tmp = 0;
          int i;
      
          /* start at the bottom of the array and work towards the top
           *
           * multiply the existing array value by 10, then add new value.
           * carry over remainder as you work back towards the top of the array
           */
          for (i = (nElements-1); (i >= 0); i--)
          {
              tmp = (pHexArray[i] * 10) + carryover;
              pHexArray[i] = tmp % 16;
              carryover = tmp / 16;
          }
      }
      
      static int *
      initHexArray (char * pDecStr, int * pnElements)
      {
          int * pArray = NULL;
          int lenDecStr = strlen (pDecStr);
          int i;
      
          /* allocate an array of integer values to store intermediate results
           * only need as many as the input string as going from base 10 to
           * base 16 will never result in a larger number of digits, but for values
           * less than "16" will use the same number
           */
      
          pArray = (int *) calloc (lenDecStr,  sizeof (int));
      
          for (i = 0; i < lenDecStr; i++)
          {
              addDecValue (pArray, lenDecStr, pDecStr[i] - '0');
          }
      
          *pnElements = lenDecStr;
      
          return (pArray);
      }
      
      static void
      printHexArray (int * pHexArray, int nElements)
      {
          int start = 0;
          int i;
      
          /* skip all the leading 0s */
          while ((pHexArray[start] == 0) && (start < (nElements-1)))
          {
              start++;
          }
      
          for (i = start; i < nElements; i++)
          {
              printf ("%c", HexChar[pHexArray[i]]);
          }
      
          printf ("\n");
      }
      
      int
      main (int argc, char * argv[])
      {
          int i;
          int * pMyArray = NULL;
          int nElements;
      
          if (argc < 2)
          {
              printf ("Usage: %s decimalString\n", argv[0]);
              return (-1);
          }
      
          pMyArray = initHexArray (argv[1], &nElements);
      
          printHexArray (pMyArray, nElements);
      
          if (pMyArray != NULL)
              free (pMyArray);
      
          return (0);
      }
      

      【讨论】:

      • 不错的解决方案。与内存使用相关的一种“优化”是对每个数字使用字节(char 或 unsigned char)而不是完整的整数。
      • 大多数问过我这类问题的面试官都希望我提出一个没有额外分配的破坏性解决方案(在我提出这样的解决方案之后)。这个问题有可能吗?
      【解决方案5】:

      Python:

      >>> from string import upper
      >>> input = "1234567890987654321234567890987654321234567890987654321"
      >>> output = upper(hex(int(input)))[2:-1]
      >>> print output
      CE3B5A137DD015278E09864703E4FF9952FF6B62C1CB1
      

      【讨论】:

      • OP 说这是一个面试问题。面对“愚蠢”的面试问题时,我(几乎)总是回答“愚蠢”的答案。它往往会开始对话的下一部分。 OP 选择 C ​​作为语言......但从未在答案中提到这是“必需的”。大多数本质上是“算法”的面试问题往往是独立于实现的。在输入中使用“任意大”的问题意味着(对我而言)解决方案将基于文本/字符操作......所以我选择了一种具有合理文本操作内置的语言。
      • 在我看来,问题的精神和给出的标签意味着一个可接受的解决方案不会使用任意大小的数字类型,无论是内置的还是库。 OP的问题也明确了这一限制。如果您的建议是反对“愚蠢”的面试问题,那么这是评论而不是答案。如果您的建议是从一个有效、简单、真实的解决方案开始,然后通过更多限制性的解决方案(例如,您需要这个实用程序在一个只有 C 运行时且没有库的微控制器上工作),那么这只是第 1 步,共 3 步。
      • @MerlynMorgan-Graham 同时,这个问题并没有真正要求任何特定的语言。
      • @ArtOfWarfare:标签上写着 C。 “但是作为一个面试问题,它不需要使用外部库”这句话暗示了他们正在寻找的抽象级别,当谈论 C 时,它实际上是处理器字节或字上的指针算术。为了进一步突出我读懂面试官思想的能力(/tongue-in-cheek),他们专门提出这样的问题,以确保你“得到”指针。
      • @MerlynMorgan-Graham - 我同意你对 OP 需要什么是正确的,但没有必要否决这个答案。很多人,比如我自己,在寻找转换碱基的算法时都会想到这一点。我对语言的细节不感兴趣,以至于当你不确定这个数字有多长时,我只是想看看如何做到这一点。使用 Python 的搜索者可能会发现这很有用 - 遗憾的是,我的项目使用的是 Obj-C,而不是 Python。
      【解决方案6】:

      我写了一个article,它描述了一个简单的 Python 解决方案,可用于将一系列数字从任意数基转换为任意基数。我最初在 C 中实现了该解决方案,并且我不希望依赖于外部库。我认为你应该能够用 C 或任何你喜欢的方式重写非常简单的 Python 代码。

      以下是 Python 代码:

      import math
      import string
      
      def incNumberByValue(digits, base, value):
         # The initial overflow is the 'value' to add to the number.
         overflow = value
         # Traverse list of digits in reverse order.
         for i in reversed(xrange(len(digits))):
            # If there is no overflow we can stop overflow propagation to next higher digit(s).
            if not overflow:
               return
            sum = digits[i] + overflow
            digits[i] = sum % base
            overflow = sum / base
      
      def multNumberByValue(digits, base, value):
         overflow = 0
         # Traverse list of digits in reverse order.
         for i in reversed(xrange(len(digits))):
            tmp = (digits[i] * value) + overflow
            digits[i] = tmp % base
            overflow = tmp / base
      
      def convertNumber(srcDigits, srcBase, destDigits, destBase):
         for srcDigit in srcDigits:
            multNumberByValue(destDigits, destBase, srcBase)
            incNumberByValue(destDigits, destBase, srcDigit)
      
      def withoutLeadingZeros(digits):
         for i in xrange(len(digits)):
            if digits[i] != 0:
               break
         return digits[i:]
      
      def convertNumberExt(srcDigits, srcBase, destBase):
         # Generate a list of zero's which is long enough to hold the destination number.
         destDigits = [0] * int(math.ceil(len(srcDigits)*math.log(srcBase)/math.log(destBase)))
         # Do conversion.
         convertNumber(srcDigits, srcBase, destDigits, destBase)
         # Return result (without leading zeros).
         return withoutLeadingZeros(destDigits)
      
      
      # Example: Convert base 10 to base 16
      base10 = [int(c) for c in '1234567890987654321234567890987654321234567890987654321']
      base16 = convertNumberExt(base10, 10, 16)
      # Output list of base 16 digits as HEX string.
      hexDigits = '0123456789ABCDEF'
      string.join((hexDigits[n] for n in base16), '')
      

      【讨论】:

      • “此主题尚不存在”。
      • 对不起,被引用的文章已移至here。您介意再次“撤销”您的反对票吗?
      • 如果您编辑帖子以包含相关代码示例以防止链接腐烂,我会支持您。
      【解决方案7】:

      这是上面提到的用javascript实现的算法:

      function addDecValue(hexArray, value) {
        let carryover = value;
        for (let i = (hexArray.length - 1); i >= 0; i--) {
          let rawDigit = ((hexArray[i] || 0) * 10) + carryover;
          hexArray[i] = rawDigit % 16;
          carryover = Math.floor(rawDigit / 16);
        }
      }
      
      function toHexArray(decimalString) {
        let hexArray = new Array(decimalString.length);
        for (let i = 0; i < decimalString.length; i++) {
          addDecValue(hexArray, Number(decimalString.charAt(i)));
        }
        return hexArray;
      }
      
      function toHexString(hexArray) {
        const hexDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
        let result = '';
        for (let i = 0; i < hexArray.length; i++) {
          if (result === '' && hexArray[i] === 0) continue;
          result += hexDigits[hexArray[i]];
        }
        return result
      }
      
      toHexString(toHexArray('1234567890987654321234567890987654321234567890987654321'));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-09
        • 1970-01-01
        • 1970-01-01
        • 2019-11-27
        • 1970-01-01
        相关资源
        最近更新 更多