Roman numerals to Decimal converter using arrays

I have been able to implement my code to work with unit numbers(less than 10) as well single digit numbers. I am currently struggling with general conversion i.e mmcccxl(2340). I think one of the things that I'm not currently able to do is compare individual characters from input to the arrays as well using the progressive sum variable declared.

This is what I have so far

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*	Roman.cpp: Convert roman numerals to decimal equivalent. Must use 
        arrays
	Highest valid number is 3999(MMMCMXCIX)
	Should be able to convert single digit numbers: 10, 70, 1000
	Also convert multi-digit numbers: 1234, 556 etc
	Able to do the inverse conversion(from decimal to roman)
*/


#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cctype>

using namespace std;

void convertToUpper(string &s);

int main()
{
	string I[] = { "","I","II","III","IV","V","VI","VII","VIII","IX" };
	string X[] = { "","X","XX","XXX","XL","L","LX","LXX","LXXX", "XC" };
	string C[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" };
	string M[] = { "","M","MM","MMM" };
	string line;
	int sum = 0;


	while (getline(cin,line)) {
		convertToUpper(line);
		for (int j = 3; j > 0; j--) {
			if (!line.compare(M[j])) {
				cout << j * 1000 << "\n";
				continue;
			}
		}
		for (int i = 9; i > 0; i--) {
			if (!line.compare(C[i])) {
				cout << i * 100 << "\n";
				continue;
			}
			if (!line.compare(X[i])) {
				cout << i * 10<< "\n";
				continue;
			}
			if (!line.compare(I[i])) {
				cout << i << "\n";
				continue;
			}
		}
		
	}
	return 0;
}

void convertToUpper(string &s) {
	for (int i = 0; i < s.length(); i++) {
		s[i] = toupper(s[i]);
	}
}



I realize I also have a lot going on in the main, but I don't think that's a major issue in this case.
Any help will be greatly appreciated.
can someone please check this out and point me to the right direction
Anyone?
closed account (SECMoG1T)
i really want to help, but i dont know what those "L,X,C,D" mean
L is meant to be 50, X 10, C 100 and D 500. Not sure if that helps
closed account (SECMoG1T)
so i came across this site.
https://www.periodni.com/roman_numerals_converter.html
am sure it outline rules to be followed when converting roman to numeric.
quite helpful.

i used those rules to come up with this function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <string>
#include <map>
#include <cctype>
#include <vector>

///bool is_valid_roman(const std::string&); define this function
std::size_t roman_to_numeral(std::string);

int main()
{
    std::vector<std::string> test{"xxix","xxx","xxxix","xl","xlviii","c","lxxxix","lxxxii","liii","xxvii","LXVIII","mmcccxl","MMMCMXCIX"};

    for(auto roman : test)
        std::cout<<roman<<" = "<<roman_to_numeral(roman)<<std::endl;
}


std::size_t roman_to_numeral(std::string roman_input)///input must be a  valid roman numeral
{
   std::map<char,std::size_t> value_lib { {'I',1}, {'V',5}, {'X',10}, {'L',50}, {'C',100}, {'D',500}, {'M',1000} };

   auto is_larger  = [](int val1, int val2) {return val1>val2;};
   auto get_value  = [&value_lib](const char& rom){ return value_lib[rom];};

   auto one_past_end = roman_input.size();
   std::size_t total_sum = 0, index = 0;

   for(auto& c : roman_input)  ///convert everything to upper
      c = std::toupper(c);

   while(index < one_past_end)
   {
       char cur_rom   = roman_input[index];
       auto cur_value = get_value(cur_rom);

       if(((index+1) < one_past_end) && is_larger(get_value(roman_input[index+1]),cur_value)) ///rule 4
       {
         auto next_value =  get_value(roman_input[index+1]);
         total_sum += (next_value - cur_value);
         index += 2;

       }

       else ///rule 3
       {
          total_sum += cur_value;
          ++index;
       }
   }

   return total_sum;
}



xxix   =   29
xxx   =   30
xxxix   =   39
xl   =   40
xlviii   =   48
c   =   100
lxxxix   =   89
lxxxii   =   82
liii   =   53
xxvii   =   27
LXVIII   =   68
mmcccxl   =   2340
MMMCMXCIX   =   3999

am not sure that would even help.
Last edited on
Yeah it does help a lot. Cheers
So I've been playing around for a bit with the code and realized there is no way to handle invalid input, i.e if you enter iphone(for instance) it does print out 1 but if you were to enter dd it prints out 1000 but from the documentation dd or vv or ll are not valid.
closed account (SECMoG1T)
there is a way, on that website, they listed rules you can use to determine if the input is a valid roman numeral, anyways i have created a function that does just that and am sure it looks quite shaggy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <iostream>
#include <string>
#include <map>
#include <vector>

bool is_valid_roman(std::string);



int main()
{
   std::vector<std::string> test{"xxix","iphone","xxx","xxxix","XXM","xl","xlviii","xyz","c","lxxxix",\
                                 "lxxxii","abcd","liii","xxvii","house","LXVIII","mmcccxl","monxxv",
                                 "MMMCMXCIX", "vv","dd","xxxx","xxx","ccc","cccc","iii","iiiii"
                                };



   for(auto roman : test)
   {
       std::cout<<roman;

       if(is_valid_roman(roman))
         std::cout<<"    --    is valid\n";

       else
         std::cout<<"    --    is invalid\n";
   }

   ///you can use it like this.
  /*
     if(is_valid_roman(number))
       ///do something with number
    else
      ///print some error
  */
}




///this function follows rules from the referenced website
bool is_valid_roman(std::string roman)
{
    auto legal = "IVXLCDM";

    for(auto& rom : roman)///to uppercase
        rom = std::toupper(rom);

    if(roman.find_first_not_of(legal) != std::string::npos)///contains invalid characters
        return false;

    std::string rule5("IXC"); ///for rule five

    for(auto rom : rule5)
    {
        if(roman.find(std::string(4,rom)) != std::string::npos) ///rule 5
            return false;
    }

    std::string rule6("VLD"); ///for rule 6
    for(auto rom : rule6)
    {
        if(roman.find(std::string(2,rom)) != std::string::npos)///rule 6
            return false;
    }

    ///each character can only precede the characters in the string "this is from a table i saw here"
    ///goo.gl/RTDazU
    std::map<char,std::string> rule7{ {'I',"IVX"},{'X',"IVXLC"},{'C',"IXLCDM"} };
    std::string check_case = "IXC";

    std::size_t index  = 0;
    auto one_past_last = roman.size();

    while(index < one_past_last) ///precedence rule
    {
        auto rom = roman[index];

        if(((index+1) < one_past_last) && (check_case.find(rom)!=std::string::npos))
        {
          auto next_rom = roman[index+1];
          auto preceds = rule7[rom];

          if(preceds.find(next_rom) == std::string::npos)
            return false;
        }

        ++index;
    }

    return true;
}



xxix    --    is valid
iphone    --    is invalid
xxx    --    is valid
xxxix    --    is valid
XXM    --    is invalid
xl    --    is valid
xlviii    --    is valid
xyz    --    is invalid
c    --    is valid
lxxxix    --    is valid
lxxxii    --    is valid
abcd    --    is invalid
liii    --    is valid
xxvii    --    is valid
house    --    is invalid
LXVIII    --    is valid
mmcccxl    --    is valid
monxxv    --    is invalid
MMMCMXCIX    --    is vali
vv    --    is invalid
dd    --    is invalid
xxxx    --    is invalid
xxx    --    is valid
ccc    --    is valid
cccc    --    is invalid
iii    --    is valid
iiiii    --    is invalid
Last edited on
Topic archived. No new replies allowed.