Подтвердить что ты не робот

Реализация atoi в C

Я не могу понять следующий код реализации atoi, особенно эту строку:

k = (k << 3) + (k << 1) + (*p) - '0';

Вот код:

int my_atoi(char *p) {
    int k = 0;
    while (*p) {
        k = (k << 3) + (k << 1) + (*p) - '0';
        p++;
     }
     return k;
}

Может кто-нибудь объяснить это мне?

Другой вопрос: каким должен быть алгоритм atof реализации?

4b9b3361

Ответ 1

k = (k << 3) + (k << 1);

означает

k = k * 2³ + k * 2¹ = k * 8 + k * 2 = k * 10

Помогает ли это?

Термин *p - '0' добавляет значение следующей цифры; это работает, потому что C требует, чтобы цифровые символы имели последовательные значения, так что '1' == '0' + 1, '2' == '0' + 2 и т.д.

Что касается вашего второго вопроса (atof), это должен быть его собственный вопрос, и это тема для диссертации, а не что-то простое ответить...

Ответ 2

<< - битовое смещение, (k<<3)+(k<<1) - это k*10, написанный кем-то, кто считал его более умным, чем компилятор (ну, он ошибался...)

(*p) - '0' вычитает значение символа 0 из символа, на который указывает p, эффективно преобразовывая символ в число.

Я надеюсь, что вы можете выяснить все остальное... просто помните, как работает десятичная система.

Вот спецификация для стандартной функции atoi. Извините, что не цитируете стандарт, но это будет работать так же хорошо (с: http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/)

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

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

Если первая последовательность непробельных символов в str не является допустимым целым числом или если такой последовательности не существует, поскольку либо str пуста, либо содержит только пробельные символы, преобразование не выполняется и возвращается ноль.

Ответ 3

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

double atof(const char *string);

int debug=1;

int main(int argc, char **argv)
{
    char *str1="3.14159",*str2="3",*str3="0.707106",*str4="-5.2";
    double f1,f2,f3,f4;
    if (debug) printf("convert %s, %s, %s, %s\n",str1,str2,str3,str4);
    f1=atof(str1);
    f2=atof(str2);
    f3=atof(str3);
    f4=atof(str4);

    if (debug) printf("converted values=%f, %f, %f, %f\n",f1,f2,f3,f4);
    if (argc > 1)
    {
        printf("string %s is floating point %f\n",argv[1],atof(argv[1]));
    }
}

double atof(const char *string)
{
    double result=0.0;
    double multiplier=1;
    double divisor=1.0;
    int integer_portion=0;

    if (!string) return result;
    integer_portion=atoi(string);

    result = (double)integer_portion;
    if (debug) printf("so far %s looks like %f\n",string,result);

    /* capture whether string is negative, don't use "result" as it could be 0 */
    if (*string == '-')
    {
        result *= -1; /* won't care if it was 0 in integer portion */
        multiplier = -1;
    }

    while (*string && (*string != '.'))
    {
        string++;
    }
    if (debug) printf("fractional part=%s\n",string);

    // if we haven't hit end of string, go past the decimal point
    if (*string)
    {
        string++;
        if (debug) printf("first char after decimal=%c\n",*string);
    }

    while (*string)
    {
        if (*string < '0' || *string > '9') return result;
        divisor *= 10.0;
        result += (double)(*string - '0')/divisor;
        if (debug) printf("result so far=%f\n",result);
        string++;
    }
    return result*multiplier;
}

Ответ 4

Интересно, что страница man atoi не указывает на установку errno, поэтому, если вы говорите любое число > (2 ^ 31) -1, вам не повезло и аналогично для чисел меньше -2 ^ 31 (предполагая 32-битный int). Вы получите ответ, но это не то, что вы хотите. Здесь тот, который может принимать диапазон - ((2 ^ 31) -1) до (2 ^ 31) -1 и возвращать INT_MIN (- (2 ^ 31)), если по ошибке. errno затем может быть проверен, чтобы проверить, не переполнено ли оно.

#include <stdio.h>
#include <errno.h>  /* for errno */
#include <limits.h> /* for INT_MIN */
#include <string.h> /* for strerror */

extern int errno;

int debug=0;
int atoi(const char *c)
{
    int previous_result=0, result=0;
    int multiplier=1;

    if (debug) printf("converting %s to integer\n",c?c:"");
    if (c && *c == '-')
    {
        multiplier = -1;
        c++;
    }
    else
    {
        multiplier = 1;
    }
    if (debug) printf("multiplier = %d\n",multiplier);
    while (*c)
    {
        if (*c < '0' || *c > '9')
        {
            return result * multiplier;
        }
        result *= 10;
        if (result < previous_result)
        {
            if (debug) printf("number overflowed - return INT_MIN, errno=%d\n",errno);
            errno = EOVERFLOW;
            return(INT_MIN);
        }
        else
        {
            previous_result *= 10;
        }
        if (debug) printf("%c\n",*c);
        result += *c - '0';

        if (result < previous_result)
        {
            if (debug) printf("number overflowed - return MIN_INT\n");
            errno = EOVERFLOW;
            return(INT_MIN);
        }
        else
        {
            previous_result += *c - '0';
        }
        c++;
    }
    return(result * multiplier);
}

int main(int argc,char **argv)
{
    int result;
    printf("INT_MIN=%d will be output when number too high or too low, and errno set\n",INT_MIN);
    printf("string=%s, int=%d\n","563",atoi("563"));
    printf("string=%s, int=%d\n","-563",atoi("-563"));
    printf("string=%s, int=%d\n","-5a3",atoi("-5a3"));
    if (argc > 1)
    {
        result=atoi(argv[1]);
        printf("atoi(%s)=%d %s",argv[1],result,(result==INT_MIN)?", errno=":"",errno,strerror(errno));
        if (errno) printf("%d - %s\n",errno,strerror(errno));
        else printf("\n");
    }
    return(errno);
}

Ответ 5

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

int atoi(char *s)
{    
    int c=1, a=0, sign, start, end, base=1;
//Determine if the number is negative or positive 
    if (s[0] == '-')
        sign = -1;
    else if (s[0] <= '9' && s[0] >= '0')
        sign = 1;
    else if (s[0] == '+')
        sign = 2;
//No further processing if it starts with a letter 
    else 
        return 0;
//Scanning the string to find the position of the last consecutive number
    while (s[c] != '\n' && s[c] <= '9' && s[c] >= '0')
        c++;
//Index of the last consecutive number from beginning
    start = c - 1;
//Based on sign, index of the 1st number is set
    if (sign==-1)       
        end = 1;
    else if (sign==1)
        end = 0;
//When it starts with +, it is actually positive but with a different index 
//for the 1st number
    else
    { 
        end = 1;
        sign = 1;
    }
//This the main loop of algorithm which generates the absolute value of the 
//number from consecutive numerical characters.  
    for (int i = start; i >=end ; i--)
    {
        a += (s[i]-'0') * base;
        base *= 10;
    }
//The correct sign of generated absolute value is applied
    return sign*a;
}

Ответ 6

о коде подсказки atoi() отсюда:

и основываясь на atoi(), моя реализация atof():

[имеют то же ограничение исходного кода, не проверяет длину и т.д.]

double atof(const char* s)
{
  double value_h = 0;
  double value_l = 0;
  double sign = 1;

  if (*s == '+' || *s == '-')
  {
    if (*s == '-') sign = -1;
    ++s;
  }

  while (*s >= 0x30 && *s <= 0x39)
  {
    value_h *= 10;
    value_h += (double)(*s - 0x30);
    ++s;
  }

  // 0x2E == '.'
  if (*s == 0x2E)
  {
    double divider = 1;
    ++s;
    while (*s >= 0x30 && *s <= 0x39)
    {
      divider *= 10;
      value_l *= 10;
      value_l += (double)(*s - 0x30);
      ++s;
    }
    return (value_h + value_l/divider) * sign;
  }
  else
  {
    return value_h * sign;
  }
}