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
, orhexadecimal
, using the0
and0x
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!
2 Answers 2
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 #include
s
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";
}
-
\$\begingroup\$ ,the code looks wonderful. Could you just help me understand the
for
loop: the,n
must be deduced as anstring
from vectorinputNumbers
, 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– Ziezi2015年07月02日 10:29:09 +00:00Commented Jul 2, 2015 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– Ziezi2015年07月02日 10:34:57 +00:00Commented Jul 2, 2015 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 compilersclang
andg++
(which is what I use) support it. \$\endgroup\$Edward– Edward2015年07月02日 23:59:07 +00:00Commented Jul 2, 2015 at 23:59
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";
}
-
\$\begingroup\$ thanks for the answer, I gave you
+1
, cause I ended up usingfor
loop similar to yours :) \$\endgroup\$Ziezi– Ziezi2015年07月02日 10:37:33 +00:00Commented Jul 2, 2015 at 10:37 -
\$\begingroup\$ @simplicisveritatis: You're welcome :) \$\endgroup\$barak manos– barak manos2015年07月02日 11:25:24 +00:00Commented Jul 2, 2015 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– Elviss Strazdins2018年12月25日 23:01:10 +00:00Commented Dec 25, 2018 at 23:01
0
is always octal. And it doe snot matter. \$\endgroup\$0x4
is not67
its just4
\$\endgroup\$hex:0, oct:0x
- Hex is0x
and Oct is0
. \$\endgroup\$