1

我需要想办法在标准 C 中执行以下操作。假设我们得到一个字符串,表示一个十六进制数字,比如 n 位。我们希望将其转换为以十进制表示相同数字的字符串。字符串可以有任意位数。至少很容易推断出十进制字符串需要 <= 2n 个字母。

一旦我们达到机器可以处理的数字的大小限制,那么字符串的长度就变得无关紧要了,所以一个 100 位的十六进制数字应该和一个 1000 位的数字一样容易/难以转换,即相同的算法应该两者都工作,因为它必须分块工作。

这里有人想过这个吗?真正令人讨厌的是 2 的大幂次的非零数字一直到一阶,所以十六进制序列中的任何元素都会影响十进制的最后一位数字......

我只能找到有人希望将字符串转换为数字或将数字转换为某个基数的字符串的问题。如果有人想知道我为什么需要这个。我在玩任意精度的数字,存储数字的最经济的格式是以 256 为基数,所以我需要打印以这种形式(或等效的任何十六进制)给出的数字。

编辑:所以我实现了一个转换功能,如果其他人可能需要它,我会在这里分享。这是非常快速的一个肮脏的,更好的实现当然会从用动态分配的东西替换结构中的数字缓冲区开始。由于所有算术都发生在“add”函数中,因此如果加法溢出,则很容易将此缓冲区重新分配到更大的位置。输出缓冲区大小总是很容易分配,因为任何十六进制数都转换为小于或等于两倍数字的十进制数。

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

#define MAXLEN 1000


struct number {
    unsigned char digits[MAXLEN];
    unsigned int num_digits;
};

int add(struct number *, struct number *, struct number *);
int mult(struct number *, struct number *, struct number *);
int power(struct number *, unsigned int, struct number *);
void print_number(struct number *, char *);
void dec(struct number *);

void hex2dec(char *hex, char *outbuf)
{
    int n;
    char *s;
    struct number decrep;
    struct number twopow;
    struct number digit;

    decrep.num_digits = 0;

    n = strlen(hex);
    s = hex;

    while(--n > -1) {
        /* weight of digit */
        twopow.num_digits = 2;
        twopow.digits[0] = 6;
        twopow.digits[1] = 1;
        power(&twopow, n, &twopow);

        /* Extract digit */
        if(*s <= '9' && *s >= '0') {
            digit.digits[0] = *s - '0';
            digit.num_digits = 1;
        } else if(*s <= 'f' && *s >= 'a') {
            digit.digits[0] = *s - 'a';
            digit.digits[1] = 1;
            digit.num_digits = 2;
        } else if(*s <= 'F' && *s >= 'A') {
            digit.digits[0] = *s - 'A';
            digit.digits[1] = 1;
            digit.num_digits = 2;
        }

        s++;

        mult(&digit, &twopow, &digit);
        add(&decrep, &digit, &decrep);
    }

    /* Convert decimal number to a string */
    if(decrep.num_digits == 0) {
        *outbuf = '0';
        *(++outbuf) = '\0';
        return;
    }

    for(n = decrep.num_digits-1; n >= 0; n--) {
        *(outbuf++) = '0' + decrep.digits[n];
    }

    *outbuf = '\0';
}

int main(void)
{
    char buf[1000];

    hex2dec("FFEa4334234FABCD", buf);

    printf("%s", buf);
    return 0;
}

void copy_number(struct number *dst, struct number *src)
{
    int i;

    for(i = 0; i < src->num_digits; i++) dst->digits[i] = src->digits[i];

    dst->num_digits = src->num_digits;
}

int power(struct number *a, unsigned int n, struct number *b)
{
    struct number atmp;

    /* Are we exponentiating by 0? */
    if(n == 0) {
        b->num_digits = 1;
        b->digits[0] = 1;
        return 0;
    }

    copy_number(&atmp, a);

    while(--n > 0) {
        mult(&atmp, a, &atmp);
    }

    copy_number(b, &atmp);
    return 0;
}

int mult(struct number *a, struct number *b, struct number *c)
{
    struct number btmp;
    struct number ctmp;
    struct number *t;

    /* Are we multiplying by 0? */
    if(a->num_digits == 0 || b->num_digits == 0) {
        c->num_digits = 0;
        return 0;
    }

    if(a->num_digits < b->num_digits) {
        t = a;
        a = b;
        b = t;
    }

    copy_number(&btmp, b);
    copy_number(&ctmp, a);

    while(1) {
        /* Are we multiplying by 1? */
        if(btmp.num_digits == 1 && btmp.digits[0] == 1) {
            break;
        }

        add(&ctmp, a, &ctmp);
        dec(&btmp);
    }

    copy_number(c, &ctmp);

    return 0;
}

int add(struct number *a, struct number *b, struct number *c)
{
    int i, j;
    int carry;

    struct number *t;

    if(a->num_digits < b->num_digits) {
        t = a;
        a = b;
        b = t;
    }

    for(i = 0, carry = 0; i < a->num_digits; i++) {

        if(i >= b->num_digits) j = a->digits[i]+carry;
        else j = a->digits[i]+b->digits[i]+carry;

        if(j > 9) {
            j -= 10;
            carry = 1;
        } else {
            carry = 0;
        }

        c->digits[i]=j;
    }

    /* Did we overflow? */
    if(carry > 0 && i == MAXLEN) return -1;

    /* Carry over from last addition? */
    if(carry > 0) {
        c->digits[i] = carry;
        c->num_digits = a->num_digits+1;
    } else {
        c->num_digits = a->num_digits;
    }

    return 0;
}

void print_number(struct number *a, char *buf)
{
    int i;

    if(a->num_digits == 0) {
        printf("0");
        return;
    }

    for(i = a->num_digits-1; i >= 0; i--) {
        *(buf++) = '0' + a->digits[i];
    }

    *buf = '\0';
}

void dec(struct number *a)
{
    int i;

    for(i = 0; i < a->num_digits; i++) {
        if(a->digits[i] > 0) {
            a->digits[i]--;
            break;
        }

        a->digits[i] = 9;
    }

    /* Did number of digits get lower */
    if(i == a->num_digits -1 && a->digits[i] == 0) {
        for(i = a->num_digits - 1; i >= 0; i--) {
            if(a->digits[i] != 0) {
                a->num_digits = i + 1;
                break;
            }
        }
    }
}
4

1 回答 1

0

请注意此代码中存在错误。只要十六进制字符串包含 0 !这个片段:

/* Extract digit */
if(*s <= '9' && *s >= '0') {
    digit.digits[0] = *s - '0';
    digit.num_digits = 1;
} else if(*s <= 'f' && *s >= 'a') {

给出数字[0] = 0;num_digites = 1; 但稍后在 mult() 函数中有 snipet :

if(a->num_digits == 0 || b->num_digits == 0) {
    c->num_digits = 0;
    return 0;
}

通过 num_digits == 0 测试 0。所以我建议在第一个函数中将代码段替换为:

if(*s == '0') {
    digit.digits[0] = *s - '0';
    digit.num_digits = 0;
}else if(*s <= '9' && *s > '0') {
    digit.digits[0] = *s - '0';
    digit.num_digits = 1;
} else if(*s <= 'f' && *s >= 'a') {

看起来对我来说很好。您可以在此站点上比较结果:http: //www.statman.info/conversions/hexadecimal.php

于 2017-05-08T13:05:33.967 回答