C++/Bse conversion
Expert: Ralph McArdell - 11/10/2004
QuestionI am wondering how i can write a program that will have the user input an integer (base 10) and the program will convert that number to other bases (2-16).... using strings i suppose?
Thanks
AnswerThe program should look something like the following outline:
- read the base 10 number (string) from the user, provide prompts and checks as required.
- convert this number string to an integer
- convert this integer back to a number string in the required base.
- output the result to the user.
Now the question is how much can we get done for us.
Well both the C and C++ standard libraries support input from the user as strings and also provide for converting such strings to integers. For example using a quick and dirty method in C++ we could just input to an integer from std::cin:
int base10Number(0);
std::cin >> base10Number;
We should check for stream errors by checking the state of std::cin:
std::cin.bad() returns true if the stream state bad bit is set. This bit indicates that the stream is corrupted or data was lost.
std::cin.fail() returns true if the stream state bad bit or fail bit are set. The fail bit indicates that an operation was not processed correctly but the input stream was generally OK. Such a case would be while reading an integer the next character is a letter.
std::cin.good() returns true if the stream state good bit is set - which indicates that all is OK.
In this case you may wish to check fail then bad. If the stream is in a fail state but not bad then you could ask the user to enter a number please. If the stream is bad you could quit the program.
You should clear the fail state before proceeding using std::cin.clear() or std::cin.clear( state ) where state indicates the state in which the stream should be in after clearing.
Now having obtained an integer from the user you need to convert it to the required base and display it to the user. C++ IOStreams support outputting integers in several common bases: octal (base 8) hexadecimal (base 16) and decimal (base 10).
You can change the base either by setting the correct flags on the stream:
std::cout.unsetf( std::ios::dec );
std::cout.setf( std::ios::hex );
std::cout << base10Number;
or by using a manipulator in conjunction with the insertion operator:
std::cout << std::hex << base10Number;
For octal output use oct in place of hex.
If you require more flexibility in the choice of base the value is displayed in then there is the non standard C function (therefore callable from C++) itoa which has the following signature:
char * itoa ( int value, char * buffer, int radix );
Where:
value is the integer value to be converted
buffer is a pointer to an array of characters to store the result
radix is the base that value should be expressed in; between 2 and 36
Note that as this is a C function it takes a pointer to a char array and it is up to you to ensure it is big enough to hold the result. Remember that C strings are zero terminated so require an extra character at the end to hold the terminator. The maximum size – for radix=2 - should be sizeof(int) * 8 + 1. For a 32-bit system this is 33 characters. You can use less for higher radix values. I leave it as a exercise for you to determine the maximum length required for any given radix value.
Also note that this is a non standard function so is only supported by some compilers / libraries.
You could then output the result:
std::cout << buffer;
If your compiler does not support itoa then you will have to roll your own. The main problem is that you probably have output running left to right, but numbers are written with the least significant digit to the right. However when determining the digits you will generate them least significant to most significant. You do this by repeatedly dividing and taking the modulus (remainder). For example if we have the value 123 and you wish it output in base 10 then we get:
Digit = 123 % 10 = 3
To Be Done = 123 / 10 = 12
Digit = 12 % 10 = 2
To Be Done = 12 / 10 = 1
Digit = 1 % 10 = 1
To Be Done = 1 / 10 = 0
A 0 To Be Done value indicates that the conversion is finished and is thus a terminating condition.
Now you can see the problem. If we just output each digit then we will get:
321
The correct digits but in the reverse order.
Note you can change the base by replacing the 10 in the calculations above - I chose 10 as it is easy for use humans to work with in examples.
There are obviously many ways to correct for this. One involves using recursion and only outputting the digits on the way back out of the recursive calls so the first digit output is the last one calculated.
If, like itoa, the digit characters are returned in a string then the string could be built in such a way as to reverse the values - the most obvious way is to reverse the string once it is built before returning the value to the caller. Or the digit characters could be appended to the string using a similar recursive method as described above.
In C++ of course we can use std::string instead of zero terminated char arrays, and this supports a reverse iterator to iterate backward through a string.
It also has many other useful functions such as appending and inserting characters that could be used to build the string in the correct order to begin with. They also take care of the memory management of the space to hold the characters. There is also a copy_backwards algorithm in the C++ standard library which may also be useful.
For a good reference and tutorial on the C++ standard library I suggest the book "The C++ Standard Library A Tutorial and Reference" by Nicolai M. Josuttis. There is a good reference on part of the C++ standard library at
http://www.sgi.com/tech/stl/