4
\$\begingroup\$

Here is what appears to be a fairly easy problem:

Write a program that prompts the user to enter several integers in any combination of octal, decimal, or hexadecimal, using the 0 and 0x base prefixes; interprets the numbers correctly; and converts them to decimal form. Then your program should output the values in properly spaced columns like this:

0x4    hexadecimal    converts to     4      decimal
0123   octal          converts to     83      decimal
65     decimal        converts to     65      decimal

Here is a first (working) draft:

const string info ("This program recognizes multiple base prefixed numbers  dec:none, hex:0x, oct:0\nand converts them into decimals.\n");
const string prompt ("Please enter numbers with random base prefixes.\n>>");

const char octprefix ('0');
const char hexprefixLower ('x');
const char hexprefixUpper ('X');
const int inputLoop = 2;

int main(){
try{
    cout << info;

    string num;
    vector<string> inputNumbers;

    int numDecimals(0);
    vector <int> inputDecimals;

    for (int i=0; i < inputLoop; i++){
        // get number with prefix 
        cout << prompt;
        cin >> num;
        inputNumbers.push_back(num);

        // return number (# of characters) back to cin stream for reinterpretation
        for (int i=0; i < num.length(); i++) cin.unget();

        // unset default flags so that prefixes are considered
        // and numbers with different bases converted directly to decimals
        cin.unsetf(ios::dec | ios::oct | ios::hex);
        cin >> numDecimals;
        inputDecimals.push_back(numDecimals);   
    }

    // discern base from prefix
    // checks first two characters of every vector element to determine base prefix
    for (int i=0; i < inputNumbers.size(); i++){
        if (inputNumbers[i][0] != octprefix){
            cout << inputNumbers[i] <<" decimal "<<" converts to "<< inputDecimals[i]  <<" decimal\n";
        }
        if (inputNumbers[1][0] == octprefix && (inputNumbers[1][1] != hexprefixLower && inputNumbers[1][1] != hexprefixUpper)){
                cout << inputNumbers[i] <<" octal "<<" converts to " << inputDecimals[i] <<" decimal\n";
        }
        if (inputNumbers[1][0] == octprefix && (inputNumbers[i][1] == hexprefixLower || inputNumbers[i][1] == hexprefixUpper)){
                cout << inputNumbers[i] <<" hexadecimal "<<" converts to " << inputDecimals[i] <<" decimal\n";  
        }
    }

}catch(exception& e){
    cerr << e.what() << endl;
    getchar();
}

getchar(); 
getchar();
return 0;
}

Are there any existent, unknown to me, language facilities that can be used to convert string to number that contains base prefix (0x, 0, i.e. numbers like: 020, 0x16, etc); facilities that recognize number base representation?

I'm trying to make it look more modular and separate it in different functions with a single task (read input, prefix recognition algorithm, print result), any ideas are welcome!

\$\endgroup\$
  • \$\begingroup\$ This task's requirements are ambiguous with regards to 0 (can be octal AND decimal). \$\endgroup\$ – barak manos Jul 1 '15 at 14:29
  • \$\begingroup\$ @barakmanos: No. 0 is always octal. And it doe snot matter. \$\endgroup\$ – Martin York Jul 1 '15 at 16:37
  • 1
    \$\begingroup\$ You know the standard input routines will do this for you automatically. \$\endgroup\$ – Martin York Jul 1 '15 at 16:37
  • 1
    \$\begingroup\$ PS. 0x4 is not 67 its just 4 \$\endgroup\$ – Martin York Jul 1 '15 at 16:38
  • 1
    \$\begingroup\$ hex:0, oct:0x - Hex is 0x and Oct is 0. \$\endgroup\$ – 23fc9a62-56de-47fb-97b4-737890 Jul 1 '15 at 19:35
4
\$\begingroup\$

I see a number of things which may help you improve your code.

Be aware of what the standard doesn't say

Your use of unget() is not portable and will not work reliably. The standard does not guarantee that an infinite number of characters will be buffered, and that's if the stream is buffered at all. See this question for more details.

Use standard library functions where available

There is already a function std::stoi() which does the conversion you seek. That is, it converts a std::string into an integer, applying the base conversions as specified.

Create functions to clarify your code

One thing that the code must do is to identify which base is specified by the source string. Since what you need is a string identifying the base, such as "hexadecimal", given an input number as a string, we can write something simple like this:

const std::string& baseid(const std::string& str)
{
    static const std::string bases[]{
        "decimal", "octal", "hexadecimal"
    };
    enum baseid { DEC, OCT, HEX };
    if ((str.length() < 2) || (str[0] != '0'))
        return bases[DEC];
    return str[1] == 'x' ? bases[HEX] : bases[OCT];
}

Don't abuse using namespace std

Putting using namespace std at the top of every program is a bad habit that you'd do well to avoid. Know when to use it and when not to (as when writing include headers).

Eliminate return 0 at the end of main

When a C++ program reaches the end of main the compiler will automatically generate code to return 0, so there is no reason to put return 0; explicitly at the end of main.

Don't wrap entire programs in a try clause

Rather than take the lazy way out and have a single massive block of code within the try clause, it's generally better to isolate specific things that might fail and deal with them individually and intelligently. If you're not going to do anything useful with the output, it's probably better not to even have a try ... catch construct.

Use the appropriate #includes

This code uses a number of things that are in specific #include files but the code (as posted) does not show them. Generally, reviewers have an easier time if you post an entire compilable program instead of just a non-compilable extract.

An alternative approach

Using the baseid function shown above, here's a much simpler program to accomplish this this:

numbase.cpp

#include <iostream>
#include <string> 
#include <vector>


const std::string& baseid(const std::string& str)
{
    static const std::string bases[]{
        "decimal", "octal", "hexadecimal"
    };
    enum baseid { DEC, OCT, HEX };
    if ((str.length() < 2) || (str[0] != '0'))
        return bases[DEC];
    return str[1] == 'x' ? bases[HEX] : bases[OCT];
}

int main(){
    const std::string info ("This program recognizes multiple base prefixed numbers  dec:none, hex:0, oct:0x\nand converts them into decimals.\n");
    std::cout << info;
    std::string num;
    std::vector<std::string> inputNumbers;

    while (std::cin >> num) 
        inputNumbers.push_back(num);
    for (const auto &n : inputNumbers)
        std::cout << n << " " << baseid(n) <<" converts to " 
                << stoi(n,0,0) <<" decimal\n";  
}
\$\endgroup\$
  • \$\begingroup\$ ,the code looks wonderful. Could you just help me understand the for loop: the, n must be deduced as an string from vector inputNumbers, right ? (It's being underlined (on my MV C++ 2010 Express)as error with the following message: cannot deduce auto type (initializer required)) \$\endgroup\$ – Ziezi Jul 2 '15 at 10:29
  • \$\begingroup\$ just made a small change: for(size_t i = 0; i < inputNumbers.size(); i++) cout << inputNumbers[i] <<" "<< baseId(inputNumbers[i]) <<" converts to "<< stoi(inputNumbers[i],0,0) <<" decimal\n"; and everything is fine. \$\endgroup\$ – Ziezi Jul 2 '15 at 10:34
  • 1
    \$\begingroup\$ The for construct I'm using is called a range-for and is a feature introduced in the 2011 version of the C++ standard. So, it's entirely possible your 2010 vintage compiler won't support it. MS VC++ 2012 should, if it's possible for you to change to that. Also the open source compilers clang and g++ (which is what I use) support it. \$\endgroup\$ – Edward Jul 2 '15 at 23:59
2
\$\begingroup\$

Identify the base of the input number in a separate function:

string GetBase(string number)
{
    if (number[0] == '0' && number.size() > 1)
    {
        if (number[1] == 'x')
        {
            return "hexadecimal";
        }
        return "octal";
    }
    return "decimal";
}

Then use it instead of those three if statements, as follows:

for (int i=0; i<inputNumbers.size(); i++)
{
    cout << inputNumbers[i] << " " << GetBase(inputNumbers[i]);
    cout << " converts to " << inputDecimals[i] << " decimal\n";
}
\$\endgroup\$
  • \$\begingroup\$ thanks for the answer, I gave you +1, cause I ended up using for loop similar to yours :) \$\endgroup\$ – Ziezi Jul 2 '15 at 10:37
  • \$\begingroup\$ @simplicisveritatis: You're welcome :) \$\endgroup\$ – barak manos Jul 2 '15 at 11:25
  • \$\begingroup\$ This is not correct. Your code will return "decimal" for zero, but C (6.4.4.1 Integer constants) and C++ (2.13.2 Integer literals) standard states that 0 is an octal constant \$\endgroup\$ – Elviss Strazdins Dec 25 '18 at 23:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.