La conversion de chaîne en unsigned int renvoie un résultat erroné
J'ai la chaîne suivante:
sThis = "2154910440";
unsigned int iStart=atoi(sThis.c_str());
Cependant, le résultat est
iStart = 2147483647
Est-ce que quelqu'un voit mon erreur?
atoi
convertit une chaîne en int
. Sur votre système, int
correspond à 32 bits et sa valeur maximale à 2147483647. La valeur que vous tentez de convertir se situe en dehors de cette plage. La valeur renvoyée de atoi
est donc non définie. Je suppose que votre implémentation renvoie la valeur maximale de int
dans ce cas.
Vous pouvez plutôt utiliser atoll
, qui retourne un long long, ce qui garantit un minimum de 64 bits. Vous pouvez également utiliser une fonction de la famille stoi/stol/stoll
ou leurs homologues unsigned , qui généreront des rapports d'erreur utiles sur les valeurs hors limites (et les valeurs non valides) sous la forme d'exceptions.
Personnellement, j'aime bien boost::lexical_cast
. Même si cela semble un peu lourd, il peut être utilisé dans un contexte plus général. Vous pouvez l'utiliser dans des modèles et simplement transférer l'argument de type au lieu d'avoir des spécialisations
Vous devriez plutôt utiliser std::strtoul
, qui se trouve dans <cstdlib>
, qui est conçu pour les nombres non signés, a une plage plus étendue et rapporte mieux les erreurs.
Si vous souhaitez utiliser std::string
pour les entrées et les exceptions pour la gestion des erreurs, utilisez std::stoul
. Une mise en œuvre brève et très efficace serait la suivante:
#include <string>
#include <stdexcept>
inline unsigned int stoui(const std::string& s)
{
unsigned long lresult = stoul(s, 0, 10);
unsigned int result = lresult;
if (result != lresult) throw std::out_of_range();
return result;
}
Ce sera beaucoup plus rapide que istringstream
, invariant de la culture (donc pas de changements inattendus dans le comportement lorsqu'il est exécuté dans des paramètres régionaux inhabituels), entièrement portable et, à l'aide du troisième argument, vous pouvez gérer différentes bases numériques ou même détecter les préfixes 0x
et 0
.
Mais unsigned int
n'est pas nécessairement assez grand pour contenir votre valeur, utilisez donc unsigned long
et vous n'aurez plus besoin du wrapper ci-dessus.
atoi retourne un int signé, qui sur votre plate-forme a une valeur maximale de 2^31-1
.
Peu importe l’affectation de ce résultat, il sera lié au type de retour.
Les flux C++ peuvent lire les entrées non signées.
std::istringstream reader(sThis);
unsigned int val;
reader >> val;
N'oubliez pas que vous pouvez toujours écrire votre propre fonction qui fait exactement ce que vous voulez.
Ce code fonctionnera avec tout nombre compris entre -9223372036854775806 (2 ^ 63 + 1) et 9223372036854775807 (2 ^ 63-1) inclus.
Quelque chose comme ça:
long long int myAtoi ( string str ) {
long long int value = 0;
for (int i = 0; i < str.size(); i++) {
if (str[i] != '-') {
value *= 10;
value += (int) ((str[i]) - '0');
}
}
if (str.size() > 0 && str[0] == '-')
return -value;
else
return value;
}
Un unsigned int est souvent une valeur de 32 bits en C++ qui a un maximum de 4 294 967 295 . 2 154 710 440 peut donc être représentée par un unsigned int. Cependant, atoi est converti en un int signé et d'une valeur maximale de 2 147 483 647 - de sorte que votre chaîne dépasse la plage de valeurs, raison pour laquelle votre réponse est incorrecte. Vous pouvez utiliser un atoll qui convertit votre chaîne en un long long qui aura au moins 64 bits. Les tailles entières dépendent du compilateur en C++. Il est souvent préférable d’inclure le fichier d’en-tête stdint.h, puis d’utiliser uint32_t ou uint64_t, etc., pour connaître la taille de votre fichier.
vous pouvez utiliser un atol qui convertit une chaîne en long int . Pour en savoir plus, voyez man atol dans Linux.
le Prototype
#include <stdlib.h>
long atol(const char *nptr);
Malheureusement, C++ n'a pas d'implémentation intégrée pour l'analyse de unsigned int et c'est vraiment étrange.
Voici un code qui peut vous aider:
#include <stdint.h>
#include <sstream>
inline unsigned int stoui(const std::string& s)
{
std::istringstream reader(s);
unsigned int val = 0;
reader >> val;
return val;
}
// This may be not the same as stoui on some platforms:
inline uint32_t stoui32(const std::string& s)
{
std::istringstream reader(s);
uint32_t val = 0;
reader >> val;
return val;
}
Ce code le convertira avec C++ 11:
std::string sThis = "2154910440";
unsigned int iStart = static_cast<unsigned int>(std::stoul(sThis));
std::stoul
renverra un unsigned long
, qui est plus grand qu'un unsigned int
.
static_cast
le convertira dans le bon type.