sashaaero
sashaaero

Reputation: 2888

Correct way to convert string to unsigned number in C++

I tried to use:

std::string s = "-150";
unsigned int a = atoi(s.c_str());
std::cout << a;

and

std::string s = "-150";
std::istringstream reader(s);
unsigned int a;
reader >> a;
std::cout << a;

I always get 4294967146. I understand that it's a U_INT_MAX - 150.
How can I get an error if number can't be converted, cause input data was invalid? Or (at least) auto-convert to 150.
P.S.
I want to find a way without my manipulation on string.

Upvotes: 3

Views: 11839

Answers (3)

Toby Speight
Toby Speight

Reputation: 30709

It seems that even std::stoul doesn't reject negative numbers (presumably due to the requirements of strtoul, which it uses). Here's a test program to demonstrate:

#include <iostream>   // std::cout, etc.
#include <string>     // std::string, std::stoul
#include <stdexcept>  // std::invalid_argument, std::out_of_range
#include <limits>     // std::numeric_limits

int main(int, char **argv)
{
    while (*++argv) {
        std::string str(*argv);
        try {
            unsigned long u = std::stoul(str);
            if (u > std::numeric_limits<unsigned int>::max())
                throw std::out_of_range(str);

            unsigned int i = u;
            std::cout << "Value = " << i << std::endl;
        } catch (const std::invalid_argument& e) {
            std::cout << "Input could not be parsed: " << e.what() << std::endl;
        } catch (const std::out_of_range& e) {
            std::cout << "Input out of range: " << e.what() << std::endl;
        }
    }
}

Running this with arguments 150 -150 abc gives

Value = 150
Input out of range: -150
Input could not be parsed: stoul

But, if we remove the std::numeric_limits test, we get

Value = 150
Value = 4294967146
Input could not be parsed: stoul

TL;DR

Read as an unsigned long and test the range yourself (wrap it up in a function - perhaps even a template function - if you find you need it in many places).

Upvotes: 4

πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

How can I get an error if number can't be casted, cause input data was invalid?

You can read into a signed int variable in 1st place:

unsigned int a = 0;
int a_input = atoi(s.c_str());
if(a_input < 0) {
    std::err << "Invalid input." << std::endl;
}
else {
    a = a_input;
}

If you need to cover the full range of unsigned int input, choose the next bigger data type available:

long a_input = atol(s.c_str());

Or (at least) auto-convert to 150.

That's not a matter of casting. You should simply use the std::abs() function to achieve what you want:

unsigned int a = std::abs(atoi(s.c_str()));
              // ^^^^^^^^ 

Upvotes: 2

Youka
Youka

Reputation: 2705

You expect a number as input and want to check it fits in unsigned int, then do it so. Convert the string to an universal data type for numbers (signed, floating point, much room -> double) and check if it can be casted without precision lost to your wished type.

You want an integer with no sign, so your number has to be positive, is in given range (so far see std::numeric_limits) and has no decimal part.

Upvotes: 3

Related Questions