C++ converting base 10 to Roman numerals

  • Thread starter Thread starter Math Is Hard
  • Start date Start date
  • Tags Tags
    Base C++
Click For Summary
The discussion centers on converting base 10 numbers to Roman numerals using C++. The original poster struggles with implementing a loop to handle the conversion efficiently without using arrays, as they are not yet familiar with that concept. Suggestions include using functions to encapsulate repeated logic and considering a while loop for subtraction instead of division for better efficiency. Participants also discuss the importance of maintaining the correct order of Roman numeral symbols during conversion and share various coding approaches, emphasizing clarity and elegance in the code. Ultimately, the conversation highlights the challenges and strategies involved in programming this conversion task effectively.
  • #31
Actually, my method does not use any strings until the actual printing, unlike yours.

And I don't scan it before printing; I simply do a check each time I print a 5-ish character.

As for how short the code is, it can be this short:

Code:
# include <iostream>
using namespace std;
int main()
{
	int num, i, ii;
	int currentDivide[7] = {1000, 500, 100, 50, 10, 5, 1};
	int count[7];
	char characters[7] = {'M', 'D', 'C', 'L', 'X', 'V', 'I'};
	cin >> num;
	for(i = 0; i < 7; i++){
		count[i] = num / currentDivide[i];
		num %= currentDivide[i];
	}
	for(i = 0; i < 7; i++)
		if(count[i] != 4 || i == 0)
			for(ii = 0; ii < count[i]; ii++)
				cout << characters[i];
		else
			cout << characters[i] << characters[i-1];
	return 0;
}

It's possible there's a bug in this code since I haven't tested it, but I think you'll agree it's fairly elegant. I could have used more lines of code to make the program more efficient in the print loop, but I decided against it.
 
Last edited:
Physics news on Phys.org
  • #32
It would be even more elegant if it actually worked. :wink:

I usually prefer code that's straightforward & easy to read, but if you want tricky, here's a "user-friendly" one that does work:
Code:
#include <iostream>
int main(){
    char l[] = "MDCLXVI";
    int r,d[]={9,5,4,1};
    cout << "Enter a positive integer to convert to Roman numerals:\n";
    cin >> r;
    do{
        int p=100;
        for(; r>=1000; r-=1000, cout << l[0]);
        for (int i=1; i<4; i++,p/=10){
            for(; r >= p*d[0]; r -= p*d[0], cout<<l[2*i]<<l[i*2-2]);
            for(; r >= p*d[1]; r -= p*d[1], cout<<l[2*i-1]);
            for(; r >= p*d[2]; r -= p*d[2], cout<<l[2*i]<<l[i*2-1]);
            for(; r >= p; r -= p, cout<<l[2*i]);
        }
        cout << "\n\nEnter the next number, or -1 to quit:\n";
        cin >> r;
    } while (r>=0);
    cout << "\n\nHave a nice day.";
    return 0;
}
 
  • #33
Oh, it doesn't work? You'll have to excuse me as I don't have a C++ compiler currently. I made some changes to it--tell me if they worked.
 
  • #34
Also, note that if you had written your code "normally," it would have been far longer than mine. As it is, it's only somewhat longer.
 
  • #35
There's still something wrong, for example:
2949
MMDCDXLVIV
 
  • #36
What's "normal"? And waddaya mean longer? You're not going to charge me for the "amenities", are you?
:smile:
 
  • #37
MMDCDXLVIV is correct for 2949.

Point is, my algorithm is inherently simpler than yours.
 
  • #38
2949=MMCMXLIX
 
  • #39
Bartholomew said:
Point is, my algorithm is inherently simpler than yours.

uh oh. them sounds like fightin' words..
 
  • #40
Bartholomew said:
Point is, my algorithm is inherently simpler than yours.

But I thought now we're going for tricky. :cry: :cry: :cry:
 
  • #41
According to that page on roman numerals, IX is incorrect... you can't have a I and then an X, it skips the V, the same reason IMM is incorrect.
 
  • #42
Math Is Hard said:
uh oh. them sounds like fightin' words..
See what you started! o:) o:)
 
  • #43
gnome said:
See what you started! o:) o:)
I know. Mea culpa.
If it's any consolation I think I failed spectacularly on the C++ midterm today. Guess what was on the test - converting to Roman f'ing numerals! :cry: I couldn't remember anything. I was like a deer in the headlights I was so scared.
 
  • #44
I didn't study that page, but I'm sure that 9 is generally written IX and not VIV.
 
  • #45
On the other hand, it's a lot more fun than studying for an exam on computabilty, complexity, etc. -- which is what I should be doing. :devil: :devil:

I'll check back later to see what Bartholomew comes up with.
 
  • #46
My algorithm is the best. Given an integer, look up its string representation in a table of strings. :smile:
 
  • #47
Well, as I said, I don't have a c++ compiler. So rather than translate this into C++ and hope I didn't make any syntax errors in doing so, I'll just post it in Java, which I wrote it in. (No user input here since that takes several lines in Java)
Code:
class RomanNumeral{
	public static void main (String argv[])
	{
		int num, i, ii;
		int cD[] = {1000, 500, 100, 50, 10, 5, 1};
		int c[] = new int[7];
		String cs = "MMMDDDCCCLLLXXXVVVIII";
		num = 2949;
		for(i=-1;i<6;c[++i]=num/cD[i],num%=cD[i]);
		for(i=0;i<7;i++)
			System.out.print(c[i]!=4||i==0?cs.substring(i*3,i*3+c[i]):""+
					cs.charAt(i*3)+cs.charAt(i*3-1));
	}
}
 
  • #48
Bartholomew said:
I'll just post it in Java
Oh no! Now everyone's posting condensed code samples in random languages – this thread is turning into the Obfuscated Roman Numeral Programming Competition... :-p

In Python (this more-or-less does what I outlined earlier (rather inefficiently :wink:); I think it's also a version of Hurkyl's method):
Code:
from sys import stdout
r = 'IVXLCDM'
print 'Decimal to Roman numeral conversions'
print 'Enter an integer 1-9999, press return or enter a non-integer to exit'
try:
    while True:
        n = int(raw_input('Your number? '))
        while (n<1) or (n>9999): n = int(raw_input('Bad number!\nYour number? '))
        if n > 999: stdout.write('M'*(n/1000))
        n = str(n % 1000)
        for (c1,c5,c10),d in [(r[i:i+3], int(n[-(i/2+1)])) for i in xrange((2*len(n)-2),-1,-2)]:
            stdout.write(['', c1, c1*2, c1*3, c1+c5, c5, c5+c1, c5+c1*2, c5+c1*3, c1+c10][d])
        stdout.write('\n')
except ValueError:
    print 'Bye!'
Batholomew said:
According to that page on roman numerals, IX is incorrect... you can't have a I and then an X, it skips the V, the same reason IMM is incorrect.
From the page linked by Integral:
The subtracted number must be no less than a tenth of the value of the number it is subtracted from. So an X can be placed to the left of a C or an L but not to the left of an M or a D.
In other words, 90 = XC not LXL, and thus 9 = IX not VIV. Just look at the page of conversions at Integral's link.
 
  • #49
You (Bartholomew) have disappointed me. :frown: :frown:

It's a few characters shorter than mine (ignoring the user chit-chat), but it's still wrong. Yes, your algorithm is simpler, but only because the language that it recognizes is simpler and as a result it fails to handle 9, 90 and 900 correctly.

Why did you think that 9 = VIV and 90 is LCL?

The Wolfram page that MathIsHard posted
http://mathworld.wolfram.com/RomanNumerals.html
clearly indicates that 9=IX and and 90=XC.

If you look at page "9" (roman-numbered) of the preface of any textbook, or the production date of any 20th century movie you'll see ix and MCM...

Bartholomew said:
According to that page on roman numerals, IX is incorrect... you can't have a I and then an X, it skips the V, the same reason IMM is incorrect.
What page were you referring to?
 
  • #50
If you don't have a Java SDE, here's a _tentative_ translation into C++. Probably this doesn't work, but hopefully you'll be able to correct the error if that's the case. (it won't be a logic error, it does work in Java) I also refined the code slightly while translating, to shave off an additional few characters and further complicate that beautiful cout of mine :smile: .

Code:
#include <iostream>
int main(){
	int num, i;
	int cD[] = {1000, 500, 100, 50, 10, 5, 1};
	int c[7];
	string cs = "IIIVVVXXXLLLCCCDDDMMM";
	cin >> num;
	for(i=-1;i<6;c[++i]=num/cD[i],num%=cD[i]);
	for(i=6;i>-1;i--)
		cout<<(c[6-i]!=4||i==6?cs.substr(i*3,c[6-i]):cs.substr(i*3+2,2));
	return 0;
}
 
  • #51
Oh, I didn't see you had posted--took me a while to do that translation. 1 moment.

Edit: Okay, you raise a valid point. I will think about how best to modify my method to do 9's, 90s, and 900s correctly. It will still be very short though.
 
Last edited:
  • #52
Alright, let me have a shot at this:

Code:
#include <iostream>
#include <cstdio>

using namespace std;

int main(int argc, char **argv)         {
	int *input = new int();
	int *rom = new int[7];
	int *steps = new int[6];
	char *map = new char[7];
	*map = 'M'; *(map+1)='D'; *(map+2)='C'; *(map+3)='L'; *(map+4)='X';
	*(map+5)='V'; *(map+6)='I';

	printf("Please enter the number that must be converted: ");
	scanf("%i", input);
	
	*rom = *input / 1000;
	*(rom + 1) = (*steps = *input % 1000) / 500;
	*(rom + 2) = (*(steps+1) = *steps % 500) / 100;
	*(rom + 3) = (*(steps+2) = *(steps+1) % 100) / 50;
	*(rom + 4) = (*(steps+3) = *(steps+2) % 50) / 10;
	*(rom + 5) = (*(steps+4) = *(steps+3) % 10) / 5;
	*(rom + 6) = (*(steps+4) % 5);
	
	printf("Here is your number in Roman numerals: ");
	for (register int i = 0; i < 6; i++) {
		for (register int k = 0; k < *(rom + i); k++) printf("%c", *(map + i));
	};

	switch(*(rom+6))	{
		case 1: printf("%s", "I"); break;
		case 2: printf("%s", "II"); break;
		case 3: printf("%s", "III"); break;
		case 4: printf("%s", "IV"); break;
		default: break;	};

	return 0;				}

Please compile Multi-threaded! :biggrin:

PS: Don't be hatin' on the printf! :smile:
 
Last edited by a moderator:
  • #53
Interesting, but not correct:

Please enter the number that must be converted: 1999
Here is your number in Roman numerals: MDCCCCLXXXXVIV

3 errors: DCCCC for 900, LXXXX for 90, VIV for 9


Edit: What's the "multithreaded" comment about?
 
  • #54
Ah crap, didn't notice that. Awesome beta testing on your part.

I meant, if your compiler supports it (I think all compilers nowadays do), like gcc 3.4.x, compile it for Multi-threaded mode (to support Intel Hyper-Threading!) and notice the performance difference. I believe the code is significantly faster if it runs multithreaded.


Btw, I found this rather succint piece of code (I'm quite sure it works) http://home.att.net/~srschmitt/script_roman.html . I feel a JScript to C/C++ conversion is in order. :-p
 
Last edited by a moderator:
  • #55
freemind said:
Btw, I found this rather succint piece of code (I'm quite sure it works) http://home.att.net/~srschmitt/script_roman.html . I feel a JScript to C/C++ conversion is in order. :-p
That's no good -- it's not obfuscated at all! :-p :-p

Here's my latest effort. It's not as short as I'd hoped it would be, but I think it's cute:
Code:
#include <iostream>
#include <string>
#include <math.h>
using namespace std;         // added this line for MSVC 6.0
#define LTRS "MDCLXVI"
int iter[]={9,5,4};
void build(int& val, int p, string& s){
    int r = (int)pow(10,p);
    while(val>=r){
        val -= r;
    s += LTRS[6-2*p];
    }
}
void trim(string t, int i, int j, string &s){
    int ind = s.find(t,0);
    string tt;
    if (j!=1)
        tt += LTRS[2*i+2];
    j == 0 ? tt += LTRS[2*i] : tt += LTRS[2*i+1];
    if (ind >=0)
       s.replace(ind, iter[j], tt);
}
int main(){
    int i, j, k, p, dec;
        string t, roman;
    cout << "Enter integer to be translated:\n";
    cin >> dec;
    for(p = 3; p >=0; p--)
        build(dec, p, roman);
    for(i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            t.erase();
            for(k = 0; k < iter[j]; k++)
                t += LTRS[2*i+2];
            trim(t,i,j,roman);
        }
    }
    cout << roman.c_str() << endl;            // edited for MSVC 6.0
    return 0;
}
 
Last edited by a moderator:
  • #56
I kinda half-understand your algorithm (it's very complex), but compiling it on Microsoft Visual C++ 7.0 and running in debug mode gives garbled output.
Did it give the correct lettering on your system?

P.S: AFAIK, the string class doesn't have operator overloading support for << (@ least not in the MSVC++ 7.0 implementation), which means 'cout << roman << endl;' threw tantrums. I had to change that to 'printf("%s", &roman);', but I doubt that's the cause of the messed result.
 
  • #57
Sorry, it worked fine in my old Dev-C++ (mingw compiler).

It also works fine in with my g++ compiler, but for that one I had to add "using namespace std;" so I edited my previous post to add that line.

I also made a small change to that final cout so now it compiles and runs correctly in MSVC 6. I don't know about MSVC 7.

Funny thing is, I have MSVC7 (I think). A few months ago I bought a student copy of Visual Studio .net but never used it. So now I'm trying, and DAMN if I can't figure out how to compile ANYTHING with it. So far all I seem to have here is the world's most complicated editor. There's buttons for everything under the sun, but no "compile", no "build", no "run". How the heck do I get this thing to do something useful?
 
Last edited:
  • #58
It works perfectly now!

I know what you mean. MSVC++.NET isn't exactly a quick-and-dirty superfast development IDE. But for large programs that use a lot of custom-built libraries, it is priceless. As for the compile, build and run buttons, it is quite silly that they don't exist in the default view. I simply use the shortcut Ctrl+Shift+B to build, and F5 to run (debug). Still, having used Borland C++ , Dev-C++ (it took forever to compile, I don't know why), and gcc/g++, I would still say that Microsoft makes the best compilers/IDEs around. Then again, I'm also known to be a M$-fanboy. :smile:

Kudos to gnome for a fine piece of code!
 
  • #59
Okay, here is my masterpiece!

Code:
#include <iostream>
int main(){
	string s = "IIIVIXXXLXCCCDCMMM";
	int n, c[] = {1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000};
	cin>>n;
	for(int i=12; i>-1;n%=c[i], i--)
		cout<<(n/c[i]>0?s.substr(i+(i+3)/4,i%4?(i-2)%4?2:1:n/c[i]):"");
}
:smile: :smile: Hopefully I translated this into C++ right; it does work in Java. If you can't get it to work I'll post the Java code which is a little longer.
 
Last edited:
  • #60
Beautiful. I changed it slightly, adding "#include<string>", changed "String" to "string", and "cin >> n"; instead of your constant n.
Code:
#include <iostream>
#include <string>
int main(){
	string s = "IIIVIXXXLXCCCDCMMM";
	int n, c[] = {1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000};
	cin >> n;
	for(int i=12; i>-1;n%=c[i], i--)
		cout<< (n/c[i]>0 ? s.substr(i+(i+3)/4, i%4 ? (i-2)%4 ? 2 : 1 : n/c[i]) : "");
}

edit: Oh, I see you already made two of those changes.

(But what if I have more than 3999 olives to count?) :-p


I've never seen a compound conditional statement like that. Is is just composed from the inside out, i.e.:
n/c>0 ? ([/color] s.substr(i+(i+3)/4, [[/color] i%4 ? [ (i-2)%4 ? 2 : 1] : n/c ][/color] ) )[/color] : ""
 
Last edited:

Similar threads

  • · Replies 22 ·
Replies
22
Views
3K
  • · Replies 3 ·
Replies
3
Views
1K
  • · Replies 3 ·
Replies
3
Views
3K
  • · Replies 3 ·
Replies
3
Views
2K
  • · Replies 10 ·
Replies
10
Views
2K
  • · Replies 2 ·
Replies
2
Views
1K
  • · Replies 3 ·
Replies
3
Views
2K
  • · Replies 15 ·
Replies
15
Views
12K
Replies
7
Views
16K
  • · Replies 7 ·
Replies
7
Views
1K