2012-05-15 3 views
1

Мне нужно выяснить способ сделать следующее в стандартном C. Предположим, нам дана строка, представляющая шестнадцатеричное число с n разрядами. Мы хотим преобразовать это в строку, представляющую одно и то же число в десятичной форме. Строки могут иметь произвольное количество цифр. Что, по крайней мере, легко вывести, так это то, что десятичной строке требуется < = 2n букв.Преобразование длинной шестнадцатеричной строки в десятичную строку

Как только мы достигнем предела размера для чисел, которые машина может обрабатывать, длина строки становится совершенно неуместной, поэтому шестнадцатеричное число со 100 цифрами должно быть таким же простым и трудно конвертируемым, как одно с 1000 цифрами, т.е. тот же алгоритм должен работать на обоих, поскольку он должен работать в кусках.

Неужели кто-нибудь подумал об этом? Что действительно раздражает, так это то, что большие полномочия 2 имеют отличные от нуля цифры до первого порядка, поэтому любой элемент в шестнадцатеричной последовательности может влиять на последнюю цифру в десятичной ...

Я мог только найти вопросы, где кто-то хотел преобразовать строку к числу или числу к строке в некоторой базе. Если кто-то задается вопросом, зачем мне это нужно. Я играю с произвольными точками точности, а самый экономичный формат для хранения чисел в базе - 256, поэтому мне нужно напечатать числа, указанные в этой форме (или эквивалентно любой шестнадцатеричной).

EDIT: Итак, я внедрил функцию преобразования, и я поделюсь ею здесь, если кому-нибудь еще понадобится. Это было очень быстро грязно, и, конечно, лучше реализовать, заменив буфера цифр в структуре тем, что получает динамическое выделение. Так как вся арифметика происходит в функции «добавить», тогда было бы легко перераспределить этот буфер на нечто большее, если произойдет переполнение. Размер выходного буфера всегда прост для назначения, поскольку любое шестнадцатеричное число преобразуется в десятичное число с меньшим или равным удвоению цифр.

#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; 
      } 
     } 
    } 
} 
+6

звучит так, как будто вам нужно google для 'c больших целых реализаций' –

+0

Я согласен с @Colin. Проблема здесь в том, что вам нужно умножить цифру n (где единицы - цифра 0) на 16^n, чтобы получить десятичный эквивалент. Так как ваше n может быть большим (100 или 1000, из вашего поста), вы должны иметь возможность делать экспоненциальность очень больших чисел, что потребует большой целочисленной реализации. –

+0

Хорошо, это было не так противно, как я думал. Поскольку вам нужно только взять полномочия с базой менее 16, мы все равно можем выполнить наивный алгоритм возведения в степень без необходимости выполнять деление на 2. – pki

ответ

0

Обратите внимание, что в этом коде есть ошибка. Как только строка HEX содержит 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; Но позже в функции мульт() есть snipet:

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

что тест 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

Смежные вопросы