27

我无法理解以下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

4

6 回答 6

33

<<是位移,(k<<3)+(k<<1)k*10由一个认为他比编译器更聪明的人写的(好吧,他错了......)

(*p) - '0'0从 指向的字符中减去字符的值p,有效地将字符转换为数字。

我希望你能弄清楚其余的......只要记住十进制系统是如何工作的。

这是标准功能的规范atoi。很抱歉没有引用标准,但这同样适用(来自:http ://www.cplusplus.com/reference/clibrary/cstdlib/atoi/ )

该函数首先根据需要丢弃尽可能多的空白字符(如 中 isspace),直到找到第一个非空白字符。然后,从这个字符开始,采用可选的初始加号或减号,后跟尽可能多的 base-10 数字,并将它们解释为数值。

字符串可以在构成整数的字符之后包含其他字符,这些字符将被忽略并且不会影响此函数的行为。

如果 in 的第一个非空白字符序列str不是有效的整数,或者由于要么 str为空或仅包含空白字符而不存在此类序列,则不执行转换并返回零。

于 2012-10-08T23:59:06.317 回答
31
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),那应该是它自己的问题,它是论文的主题,而不是简单的回答......

于 2012-10-08T23:58:51.140 回答
1
#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;
}
于 2012-12-24T06:13:08.443 回答
0

有趣的是, atoi 的手册页并没有指示设置 errno,所以如果你说的任何数字 > (2^31)-1,你就不走运了,同样对于小于 -2^31 的数字(假设 32 -位整数)。你会得到一个答案,但它不会是你想要的。这是一个范围为 -((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);
}
于 2012-12-24T04:04:07.937 回答
0

这是我的实现(已成功测试包含字母、+、- 和零开头的案例)。我试图在Visual Studio中对atoi函数进行逆向工程。如果输入字符串只包含数字字符,则可以在一个循环中实现。但它变得复杂,因为你应该照顾-+和字母。

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;
}
于 2018-10-20T13:13:59.603 回答
0

关于 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;
  }
}
于 2019-04-30T06:16:25.910 回答