将字符串转换为整数(不是atoi!)

时间:2009-04-21 20:23:07

标签: algorithm numbers

我希望能够作为输入获取指向基数2到16中的数字的字符指针,并作为第二个参数,数字所在的基数,然后将其转换为基数2中的数字。可以是任意长度的。我的解决方案现在可以实现atoi()函数的功能,但如果可以使用查找表解决方案,我很好奇,仅仅是出于学术兴趣。

我发现这对二进制,八进制和十六进制来说很简单。我可以简单地为每个数字使用查找表来获得一系列位。例如:

0xF1E ---> (F = 1111)(1 = 0001)(E = 1110)---> 111100011110

0766 ---> (7 = 111)(6 = 110)(6 = 110)---> 111110110

1000 ---> ??? ---> 1111101000

然而,我的问题是我想为奇数基数做这个查找表方法,比如基数10.我知道我可以像atoi那样编写算法并做一堆乘法和加法,但对于这个特定的问题我试图看看我是否可以用查找表来完成它。但是,对于基数10来说,这绝对不是那么明显。我很好奇是否有人有任何聪明的方法来弄清楚如何为Base X生成通用的查找表 - >基础2.我知道对于基数10,你不能一次给它一个数字,所以解决方案可能必须一次查找一组数字。

我知道乘法和加法解,但由于这些是任意长度数,所以乘法和加法运算不是免费的,所以我想尽可能避免它们。

7 个答案:

答案 0 :(得分:4)

您必须使用输入宽度为m基本b符号的查找表,返回n位,以便

n = log2(b) * m

表示正整数bnm。因此,如果b不是2的幂,则不会有(简单的)查找表解决方案。

我认为没有解决方案。以下带有基数10的示例说明了原因。

65536 = 1 0000 0000 0000 0000

将最后一位数字从6更改为5将翻转所有位。

65535 = 0 1111 1111 1111 1111

如果从最后开始处理输入,几乎相同。将第一个数字从6更改为5会翻转大量的位。

55535 = 0 1101 1000 1111 0000

答案 1 :(得分:4)

这不可能在不是2的幂的基础上转换为base-2。基础8(和16)可能的原因是转换的工作方式如下:

octal ABC = 8^2*A + 8^1*B + 8^0*C (decimal)
          = 0b10000000*A + 0b1000*B + C (binary)

所以如果你有一个A =(0b000到0b111)的查找表,那么乘法总是1和一些尾随零,所以乘法很简单(只是左移)。

然而,考虑10的“奇数”基数。当你看10的权力时:

10^1 = 0b1010
10^2 = 0b1100100
10^3 = 0b1111101000
10^4 = 0b10011100010000
..etc

你会注意到乘法永远不会变得简单,所以你不能拥有任何查找表并进行位移和ors,无论你将它们分组多大。它总是重叠的。您可以做的最好的是具有以下形式的查找表:(a,b)其中a是数字位置,b是数字(0..9)。然后,您只需减少添加n个数字,而不是乘以和添加n个数字(加上查找表的内存成本)

答案 2 :(得分:2)

算法非常简单。语言不可知是:

total = 0
base <- input_base
for each character in input:
   total <- total*base + number(char)

在C ++中:

// Helper to convert a digit to a number
unsigned int number( char ch )
{
   if ( ch >= '0' && ch <= '9' ) return ch-'0';
   ch = toupper(ch);
   if ( ch >= 'A' && ch <= 'F' ) return 10 + (ch-'A');
}
unsigned int parse( std::string const & input, unsigned int base )
{
   unsigned int total = 0;
   for ( int i = 0; i < input.size(); ++i )
   {
      total = total*base + number(input[i]);
   }
   return total;
}

当然,您应该注意可能的错误(不连贯的输入:基数2和输入字符串'af12')或任何其他异常情况。

答案 3 :(得分:2)

琴弦有多大?您可以通过执行以下操作将multiply-and-add转换为lookup-and-add:

  • 将数字0-9,10,20,30,40,... 90,100,200,... 900,1000,2000,...,9000,10000,...存储在目标中基于一张桌子。
  • 对于从最右边开始的每个字符,将其适当地索引到表中并将其添加到运行结果中。

当然我不确定这实际上会有多好,但这是一个想法。

答案 4 :(得分:0)

  • 以正在运行的计数开始。
  • 对于字符串中的每个字符(从左到右阅读)
    • 按基数计算。
    • 将字符转换为int值(0到base)
    • 为运行计数添加字符值。

答案 5 :(得分:0)

您需要多准确?

如果你正在寻找完美,那么乘法和加法真的是你唯一的办法。如果它是您应用程序中最慢的部分,我会感到非常惊讶。

如果数量级足够好,请使用查找表来查找最接近2的幂。

示例1:1234,最接近2的幂是1024。 例2:98765,最接近的是65536

你也可以通过计算位数来驱动它,并将适当的2的幂乘以最左边的数字。这可以实现为左移:

示例3:98765具有5个数字,最接近2到10000的功率是8192(2 ^ 13),因此结果是9 <&lt;&lt; 13

答案 6 :(得分:0)

  

我在你的澄清评论之前写了这个,所以它可能不太适用。我不确定是否可以使用查找表方法。如果您确实不需要任意精度,那么请利用运行时。

如果C / C ++解决方案可以接受,我相信您正在寻找的是以下内容。它可能包含边缘情况中的错误,但它确实编译并按预期工作,至少对于正数。让它真正起作用是读者的一种练习。

/*
 * NAME
 *    convert_num - convert a numerical string (str) of base (b) to
 *                  a printable binary representation
 * SYNOPSIS
 *    int convert_num(char const* s, int b, char** o)
 * DESCRIPTION
 *    Generates a printable binary representation of an input number
 *    from an arbitrary base.  The input number is passed as the ASCII
 *    character string `s'.  The input string consists of characters
 *    from the ASCII character set {'0'..'9','A'..('A'+b-10)} where
 *    letter characters may be in either upper or lower case.
 * RETURNS
 *    The number of characters from the input string `s' which were
 *    consumed by this operation.  The output string is placed into
 *    newly allocated storage which is pointed to by `*o' upon successful
 *    completion.  An error is signalled by returning `-1'.
 */
int
convert_num(char const *str, int b, char **out)
{
    int rc = -1;
    char *endp = NULL;
    char *outp = NULL;
    unsigned long num = strtoul(str, &endp, b);
    if (endp != str) { /* then we have some numbers */
        int numdig = -1;
        rc = (endp - str); /* we have this many base `b' digits! */
        frexp((double)num, &numdig); /* we need this many base 2 digits */
        if ((outp=malloc(numdig+1)) == NULL) {
            return -1;
        }
        *out = outp; /* return the buffer */
        outp += numdig; /* make sure it is NUL terminated */
        *outp-- = '\0';
        while (numdig-- != 0) { /* fill it in from LSb to MSb */
            *outp-- = ((num & 1) ? '1' : '0');
            num >>= 1;
        }
    }
    return rc;
}