Conversia șirului de caractere în int nesemnat returnează un rezultat greșit (Programare, C++, Conversie De Tip)

tmighty a intrebat.

Am următorul șir de caractere:

sThis = "2154910440";

unsigned int iStart=atoi(sThis.c_str());

Cu toate acestea, rezultatul este

iStart = 2147483647

Vede cineva greșeala mea?

Comentarii

  • Am observat că rezultatul tău este 2^31 – 1. Cred că este o problemă cu atoi() care folosește bitul cel mai mare ca bit de semn. Poate citiți cu atenție documentația atoi() pentru a vedea ce face cu semnele? –  > Por SaganRitual.
8 răspunsuri
Benjamin Lindley

atoi convertește un șir de caractere într-un int. Pe sistemul tău, un int are 32 de biți, iar valoarea sa maximă este 2147483647. Valoarea pe care încercați să o convertiți se află în afara acestui interval, astfel încât valoarea de returnare a lui atoi este nedefinită. Implementarea dvs., presupun, returnează valoarea maximă a lui an int în acest caz.

În schimb, ați putea utiliza atollcare returnează un long long, care este garantat a fi de cel puțin 64 de biți. Sau ați putea utiliza o funcție din stoi/stol/stoll sau a lor omologii fără semn, care vor oferi de fapt rapoarte de eroare utile privind valorile din afara intervalului (și valorile invalide) sub formă de excepții.

Personal, îmi place boost::lexical_cast. Chiar dacă pare un pic greoi, poate fi utilizat într-un context mai general. Îl puteți utiliza în șabloane și doar să transmiteți argumentul tip în loc să trebuiască să aveți specializări

Comentarii

  • OP nu a spus că folosește C++11.  > Por Kyle_the_hacker.
  • 18

  • @Kyle_the_hacker: Ei bine, suntem în 2013, așa că, dacă nu spune altfel, voi continua să presupun în răspunsurile mele. –  > Por Benjamin Lindley.
  • @BenjaminLindley Ceea ce ignoră oarecum realitatea. –  > Por James Kanze.
  • @JamesKanze: Ca să fiu sincer, nu aș omite informațiile despre C++11 chiar dacă OP a declarat în mod explicit că nu poate folosi C++11. Oricum, am furnizat o alternativă C++98/03, deci care este problema? –  > Por Benjamin Lindley.
  • @tmighty: Compilatorul dvs. are suport parțial pentru C++11, iar biblioteca dvs. standard are într-adevăr familia de funcții pe care am menționat-o. –  > Por Benjamin Lindley.
Ben Voigt

În schimb, ar trebui să utilizați std::strtoul, care se găsește în <cstdlib>, care este conceput pentru numere fără semn, are un interval mai mare și raportează mai bine erorile.

Dacă doriți să utilizați std::string pentru intrare și excepții pentru gestionarea erorilor, utilizați std::stoul. O implementare scurtă și foarte eficientă ar fi următoarea:

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

Aceasta va fi mult mai rapidă decât istringstream, va fi invariantă din punct de vedere cultural (deci nu se vor produce modificări neașteptate ale comportamentului atunci când este rulată într-o locație neobișnuită), va fi complet portabilă și, folosind al treilea argument, veți putea suporta diferite baze numerice sau chiar să efectuați detectarea de 0x și 0 prefixe.

Dar unsigned int nu este neapărat suficient de mare pentru a conține valoarea dumneavoastră, așa că utilizați unsigned long, și atunci nu veți avea nevoie de învelișul de mai sus.

Comentarii

  • Sunt de acord, strtoul este mai portabil –  > Por Erik Aronesty.
  • Din câte știu eu, unsigned long nu este același lucru cu unsigned int pe unele platforme și sisteme de operare. Ar trebui să aibă dimensiuni diferite în Linux pe arhitectura x86_64. –  > Por Sergey.
  • @Sergey: Da, motiv pentru care strtoul este o alegere mai bună. Se poate compara apoi valoarea înainte și după coerciție pentru a unsigned int pentru a vedea dacă este prea mare. –  > Por Ben Voigt.
  • @BenVoigt: Nu înțeleg. De ce obținerea unei valori potențial de 64 de biți și efectuarea unor verificări suplimentare înainte de a coborî la 32 de biți este mai bună decât analiza directă la unsigned 32? Văd aici doar o potențială corupție „ciudată” a datelor după conversia de la 64 la 32 de biți și avertismente ale compilatorului. –  > Por Sergey.
  • @Sergey: Aveți nevoie de unsigned long pentru a păstra această valoare în mod portabil. De asemenea, mi se pare ciudat că vă plângeți de logica de detectare a erorilor sugerată de mine, când nu furnizați absolut nicio tratare a erorilor în răspunsul dvs. –  > Por Ben Voigt.
helloworld922

atoi returnează un signed int, care pe platforma dvs. are o valoare maximă de 2^31-1.

Nu contează la ce atribuiți acel rezultat, acesta va fi delimitat de tipul de returnare.

Fluxurile C++ pot citi int-uri fără semn.

std::istringstream reader(sThis);
unsigned int val;
reader >> val;

Oleksiy

Nu uitați, puteți oricând să vă scrieți propria funcție care să facă exact ceea ce doriți.

Acest cod va funcționa cu orice număr cuprins între -9223372036854775806 (2^63+1) și 9223372036854775807 (2^63-1) inclusiv.

Ceva de genul acesta:

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;
}

Comentarii

  • Așa cum este dată, această funcție tratează semnele minus prea liber și ar trata un șir de caractere precum „—–10—-33—5-8-9-” returnând -1033589. –  > Por SO Stinks.
David Elliman

Un int fără semn este adesea o valoare de 32 de biți în C++ care are un maxim de 4.294.967.295. 2.154.710.440 poate fi reprezentat, prin urmare, ca un int fără semn. Cu toate acestea, atoi se convertește într-un int cu semn și are o valoare maximă de 2.147.483.647 – astfel, șirul de caractere depășește intervalul de valori, motiv pentru care răspunsul dvs. este incorect. Ați putea utiliza atoll, care convertește șirul de caractere într-un long long, care va avea cel puțin 64 de biți. Dimensiunile numerelor întregi depind de compilator în C++. Adesea este mai bine să includeți fișierul antet stdint.h și apoi să utilizați uint32_t sau uint64_t și așa mai departe, astfel încât să știți cu ce dimensiune aveți de-a face.

Madan Ram

puteți utiliza atol care convertește șirul de caractere în long int .Pentru mai multe informații, consultați man atol în Linux.

prototipul

#include <stdlib.h>
long atol(const char *nptr);

Serghei

Din nefericire, C++ nu are o implementare încorporată pentru analizarea unsigned int și acest lucru este foarte ciudat.

Iată un cod care vă poate ajuta:

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

Comentarii

  • Asta pentru că nimeni nu a avut nevoie de așa ceva. strtoul funcționează perfect pentru analiza oricărui cod valid unsigned int valoare validă, iar detectarea depășirii necesită doar o singură comparație suplimentară. Pe scurt, acest lucru este cu câteva ordine de mărime mai eficient decât codul stricat (lipsit complet de detectarea erorilor) pe care l-ați furnizat. –  > Por Ben Voigt.
  • BTW, downvoting pentru regurgitarea unui răspuns existent. –  > Por Ben Voigt.
  • @BenVoigt: Sugerați o soluție proastă. Din păcate, nu pot pune un exemplu de cod chiar aici, așa că mi-am actualizat răspunsul la întrebarea principală. –  > Por Sergey.
  • Nu, nu este ceea ce am sugerat. –  > Por Ben Voigt.
  • Mi-am editat răspunsul pentru a arăta ce am vrut să spun prin „Se poate apoi compara valoarea înainte și după coerciția la unsigned int pentru a vedea dacă este prea mare” – –  > Por Ben Voigt.
GizMoCuz

Acest cod îl va converti cu C++11:

std::string sThis = "2154910440";
unsigned int iStart = static_cast<unsigned int>(std::stoul(sThis));

std::stoul va returna un unsigned long, care este mai mare decât un unsigned int.

static_cast îl va converti în tipul corect.

Comentarii

  • Un unsigned long poate fi să fie mai mare decât unsigned int – dar poate avea și aceeași dimensiune. –  > Por Toby Speight.
  • Va fi static_cast va arunca o eroare, similară soluției lui @BenVoigt? –  > Por m_power.