Je ne comprends pas le code d'implémentation atoi
suivant, en particulier cette ligne: k = (k<<3)+(k<<1)+(*p)-'0';
Le code:
int my_atoi(char *p) {
int k = 0;
while (*p) {
k = (k<<3)+(k<<1)+(*p)-'0';
p++;
}
return k;
}
Est-ce que quelqu'un peut me l'expliquer?
Une autre question: quel devrait être l'algorithme de la mise en œuvre atof
?
k = (k << 3) + (k << 1);
veux dire
k = k * 2³ + k * 2¹ = k * 8 + k * 2 = k * 10
Est ce que ça aide?
Le terme *p - '0'
ajoute la valeur du chiffre suivant; cela fonctionne parce que C requiert que les caractères numériques aient des valeurs consécutives, de sorte que '1' == '0' + 1
, '2' == '0' + 2
, etc.
Quant à votre deuxième question (atof
), cela devrait être sa propre question, et c'est le sujet d'une thèse, pas quelque chose de simple à répondre ...
<<
est un décalage de bits, (k<<3)+(k<<1)
est un k*10
, écrit par quelqu'un qui, bien qu'il soit plus intelligent qu'un compilateur (enfin, il s'était trompé ...)
(*p) - '0'
soustrait 0
du caractère pointé par p
, convertissant ainsi le caractère en nombre.
J'espère que vous pourrez comprendre le reste ... rappelez-vous simplement comment fonctionne le système décimal.
Notez qu'il s'agit d'une implémentation atoi
conforme à la norme. Désolé de ne pas citer la norme, mais cela fonctionnera tout aussi bien (à partir de: http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/ )
La fonction supprime d’abord autant de caractères blancs (comme dans Isspace) que nécessaire, jusqu’à ce que le premier caractère non-blanc soit a trouvé. Ensuite, à partir de ce caractère, prend une initiale facultative signe plus ou moins suivi d'autant de chiffres en base 10 que possible, et les interprète comme une valeur numérique.
La chaîne peut contenir des caractères supplémentaires après ceux qui forment le nombre entier, qui sont ignorés et n’ont aucun effet sur le comportement de cette fonction.
Si la première séquence de caractères non-blancs dans str n'est pas un nombre entier valide, ou si une telle séquence n'existe pas, car soit str est vide ou il ne contient que des caractères d'espacement, pas de conversion est effectuée et zéro est renvoyé.
Voici mon implémentation (testée avec succès avec des cas contenant et commençant par des lettres, +, - et les zéros) . J'ai essayé de faire de la contre-ingénierie atoi dans Visual Studio. Si la chaîne d'entrée ne contient que des caractères numériques, elle peut être implémentée dans une boucle. mais cela se complique parce que vous devez vous occuper de -, + et de lettres.
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;
}
#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;
}
Il est intéressant de noter que la page de manuel de atoi n'indique pas la valeur de errno. Par conséquent, si vous parlez un nombre> (2 ^ 31) -1, vous n’êtes pas plus chanceux et de la même manière pour les nombres inférieurs à -2 ^ 31 (en supposant que 32 -bit int). Vous obtiendrez une réponse, mais ce ne sera pas ce que vous voulez. En voici une qui pourrait prendre une plage de - ((2 ^ 31) -1) à (2 ^ 31) -1 et renvoyer INT_MIN (- (2 ^ 31)) en cas d'erreur. errno pourrait alors être vérifié pour voir s'il débordait.
#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);
}