C语言 如何正确地转换为任意进制?

ix0qys7i  于 2023-02-07  发布在  其他
关注(0)|答案(3)|浏览(140)

我的代码编译成功了,但是每次运行参数./base.o 42 2(将其转换为二进制的基2)时,输出都是0,每次运行./base.o 99 5时,结果都是444重复。

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

int main(int argc, char *argv[]) {
    int remainder = 0;
    int num0 = 0;
    int i = 0;

    int num1 = atoi(argv[1]);
    int num2 = atoi(argv[2]);
    int quotient = num1;

    while (quotient != 0) {
        remainder = num1 % num2;
        quotient = quotient / num2;

        num0 = (remainder * pow(10, i)) + num0;
        i++;
    }

    printf("%d\n", num0);

    return 0;
}
lvmkulzt

lvmkulzt1#

数制只是供人类阅读的,数字是一种书写基数幂次递增多项式的简写形式。
例如,以10为底数(==基数)的数字3057为:
3 ×103 + 0 ×102 + 5 ×101 + 7 ×100
我可以完全改变基数:
B ×162 + F ×161 + 1 ×160
我们看到B(11)×16×16加上F(15)×16再加上1等于2816+240+1 = 3057 --这和前面的数字完全一样。

文本到整数

从文本表示转换为数值非常简单:

int value = 0;
for (char * s = text_number;  *s;  s++)
  value = (value * radix) + value_of( *s );

所谓“c的值”,就是说'7'应该转换成7'E'应该转换成15(因为数字的ASCII表示!=那个数字),这种事情最容易用查找表来完成。

整数到文本

另一种方法也很简单,你只需要按照正确的顺序打印数字,用一个字符串来保存这些数字就很有帮助了。

char buffer[ N ] = { 0 };   // where N is big enough to hold all the digits
char * s = buffer + N - 1;  // start at the back
while (value)
{
  *(--s) = digit_for( value % radix );
  value /= radix;
}
puts( s );

同样,“digit for”将7转换为'7',将15转换为'E'。同样,一个简单的查找表(实际上与前面的查找表完全相同)在这里很有用。
如果value是0,我们应该只使用puts( "0" ),避免循环。

边缘案例

唉,在计算领域,生活从来都不像我们想象的那么简单,我们需要处理负数、INT_MIN、零等等。
对于您正在做的事情,我并不担心,但如果有必要,您可以保留INT_MIN(或您正在处理的任何数据类型)的文本表示,以便与输入进行比较,如果输入与之匹配,则直接分配该值。
您甚至可以通过转换 * 最大 * 可能值、在字符串的最后一个字符上加1并在其前面加上减号来 * 生成 * 这个最小值表示。
当然,如果您正在处理一个 unsigned 类型,就不需要这么麻烦了。
不过,您仍然需要注意溢出。如果您得到的位数超过了结果类型所能容纳的位数...
我们看到杂草使这些东西变得有点棘手。基本的想法很简单,只要确保处理所有奇怪的边缘情况,东西变得不稳定。

gkn4icbw

gkn4icbw2#

代码中存在多个问题:

  • 程序自变量似乎以基数10表示,并且输出必须以对应于第二自变量的基数产生。
  • 为此,假定至少有2个自变量,则转换int num1 = atoi(argv[1]);int num2 = atoi(argv[2]);是正确的:你应该测试程序接收到的参数的实际数目。
  • 但是循环中的计算是不正确的,因为您使用remainder = num1 % num2;而不是remainder = quotient % num2;,所以您总是得到复制的最后一位。
  • 你的方法是非常有限的,因为你只能处理最多10位的基数和目标基数中最多10位的数字。
  • 你应该把余数存储在一个数组中,然后以相反的顺序输出这些余数。

以下是修改后的版本:

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

int main(int argc, char *argv[]) {
    int remainder[sizeof(int) * CHAR_BIT];
    int num1, num2, quotient, i;
    const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";

    if (argc > 2) {
        num1 = atoi(argv[1]);
        num2 = atoi(argv[2]);

        if (num2 < 2 || num2 > 36) {
            printf("invalid base: %d\n", num2);
            return 1;
        }
        quotient = num1;
        i = 0;
        /* using a for(;;) loop instead of `while (quotient != 0)` to
           produce at least 1 digit for quotient == 0
        */
        for (;;) {
            /* using abs to handle negative numbers all the way to INT_MIN */
            remainder[i++] = abs(quotient % num2);
            quotient = quotient / num2;
            if (quotient == 0)
                break;
        }

        if (num1 < 0) {
            putchar('-');
        }
        while (i > 0) {
            putchar(digits[remainder[--i]]);
        }
        putchar('\n');
    }
    return 0;
}
tcomlyy6

tcomlyy63#

这看起来是一个有趣的问题,在我重构初始程序的过程中,我看到其他好的替代方案已经被提供了,为了这个组合,我提供了这个重构版本的基本转换程序。

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

#define MAX 100

char derive(int digit)
{
    char value;
    if (digit < 10)
    {
        value = (char)digit + '0';
    }
    else
    {
        switch(digit)
        {
            case 10:
                value = 'A';
                break;
            case 11:
                value = 'B';
                break;
            case 12:
                value = 'C';
                break;
            case 13:
                value = 'D';
                break;
            case 14:
                value = 'E';
                break;
            case 15:
                value = 'F';
                break;
            default:
                value = '*';
                break;
        }
    }
    return value;
}

int main(int argc, char *argv[]) {
    char cnv[MAX];
    int remainder = 0;
    int num0      = 0;
    int i         = 0;

    int num1 = atoi(argv[1]);
    int num2 = atoi(argv[2]);
    int quotient = num1;

    if (num2 < 2 || num2 > 16)
    {
        printf("Currently, only base numbers from 2 to 16 are allowed\n");
        return 0;
    }

    num0 = num2;

    for (int j = 0; j < MAX; j++)           /* Initialize the character array to terminator values */
    {
        cnv[j] = '\0';
    }

    while (1)                               /* Determine the largest power of the base needed for deriving a converted value */
    {
        num0 = num0 * num2;
        if (num0 > num1)
        {
            num0 = num0 / num2;
            break;
        }
    }

    while (num0 >= num2) {                  /* Work down from the highest base power to derive the base value */
        remainder = num1 % num0;
        quotient  = quotient / num0;
        cnv[i]    = derive(quotient);
        quotient  = remainder;
        num0 = num0 / num2;
        i++;
    }

    cnv[i] = derive(remainder);             /* Finish by storing the "units" value */

    printf("%s\n", cnv);

    return 0;
}

有一些信息要指出,我做了什么,使基地转换函数。

  • 程序没有使用“math. h”包含文件中的幂函数,而是保持将基值乘以自身,直到达到小于或等于所评估数字的基的最大幂。
  • 一旦确定了最大幂,该值就用于通过执行整数除法并通过减小基数的幂来保留余数以用于后续除法来导出每个数字值。
  • 每个商值被转换为基数中的相应数字,然后存储在字符数组中,除数被基数除,直到除数值小于基数值。
  • 一旦所有除法都在“while”循环中发生,最终余数值就被转换为最终数字并放入字符数组中,然后打印字符数组以显示转换后的值。

因此,该方法不使用幂函数,而是导出所需基值的最大幂,然后通过除法和递减过程返回。
根据问题中给出的原始值测试程序,产生以下最终输出。

@Vera:~/C_Programs/Console/Base/bin/Release$ ./Base 42 2
101010
@Vera:~/C_Programs/Console/Base/bin/Release$ ./Base 99 5
344

无论如何,您可能希望将这个重构版本与其他提供的解决方案沿着进行评估,看看它是否符合您的项目的精神。

相关问题