converter

Hey guys.

Im working on this project that converts digits. I was using a block of if statements to print out all the combinations but the code I found does it in like less than 10 lines. Can anyone give me an in depth understanding of how this works? Would love to have a solid understanding of this so I can approach my code more efficiently like this next time. If someone could break it down id appreciate it.

Last edited on
It is just a lookup table, and nicely done. Its only limitations are that (0 < a < 4000).

M[] is for the thousands place (notice the integer division by 1000).
C[] is for the hundreds place (again, notice the remainder and division math).
Etc.

For each place there is a maximum of ten digit values (0..9), hence ten elements in each array.

Hope this helps.
Could you explain the math though? Like how does that one equation know to what 697 is in roman numerals? Also why the one "" empty set on every string ?
If you look at the line
ans = M[a/1000]+C[(a%1000)/100]+X[(a%100)/10]+I[(a%10)];
then this is composing a string as
(how many thousands) + (how many hundreds) + (how many tens) + (how many units)

If you needed "how many thousands" then it would be produced by a/1000 (using integer division).

If you wanted "how many hundreds" then you would first have to use the modulo operator as a%1000 to remove the preceding thousands, then /100 to find the remaining hundreds.

If you wanted "how many tens" then you would first have to use the modulo operator as a%100 to remove the preceding hundreds, then /10 to find the remaining tens.

Finally you just need a%10 to find the single digits.

Note the use of modulo (%) and integer divide (/) here.



If you need, say, 2 thousands, then you would use the string M[2]. Remember that arrays count from 0. Remember, too, that the Romans didn't use zero as a placeholder: they simply didn't write anything - hence your empty strings in the first array elements.
The Romans got away with this because, although they used a basically decimal system, the "digits" they had in each decadal range were different: hence your distinct M[], C[], X[] and I[] arrays. Nobody in Western Europe used zero until after the Middle Ages, when we started to develop a placeholder system.


For your example, a=697:
a/1000 is 0 (integer division), so we need M[0] or "".
a%1000 is 697; then 697/100 is 6 (integer division), so we need C[6] or "DC".
a%100 is 97; then 97/10 is 9 (integer division), so we need X[9] or "XC".
a%10 is 7, so we need I[7] or "VII".
Thus: DCXCVII

There weren't many great Roman scientists or mathematicians!


Actually, like @Duthomas, I think the code is nicely done. I don't know how to count higher in Roman numerals.
Last edited on
this is good stuff! As an exercise, you should also do a
1
2
3
int from_roman(string s)
{
}
I love this website you guys never fail to give me great answers. And yes icy1 i'm currently working on that right now. Its a little more complicated though since you cant approach it in the same manner as the method I posted. Or can you?
You should be able to take a similar approach. Figure out the important individual markers and make an array of them in descending value order. Parse left to right. For example, M is 1000, CM is 900, D is 500, CD is 400, etc.

Sum the values as you find them.
It actually just clicked icy I understand how it works now should be easy to do it in the same way. I'll let you know when I get it
Icy would you mind showing me your taking on doing the function in the same manner? I can't seem to figure it out
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
#include <iostream>
#include <string>
#include <vector>
using namespace std;

vector< vector<string> > ROMAN = {
   {"","I","II","III","IV","V","VI","VII","VIII","IX"},
   {"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"},
   {"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"},
   {"","M","MM","MMM"}                                  };

//=====================================================================

string noBlanks( string s )
{
   string result;
   for ( char c : s ) if ( c != ' ' ) result += c;
   return result;
}

//=====================================================================

string intToRoman( int n )
{
   if ( n <= 0 || n >= 4000 ) return "";

   string s;

   for ( int decade = 3, power = 1000; decade >= 0; decade--, power /= 10 )
   {
      n %= ( 10 * power );
      s += ROMAN[decade][n/power];
   }

   return s;
}

//=====================================================================

int romanToInt( string s )
{
   s = noBlanks( s );
   int n = 0;

   for ( int decade = 3, power = 1000; decade >= 0; decade--, power /= 10 )
   {
      for ( int j = ROMAN[decade].size()-1; j > 0; j-- )
      {
         string test = ROMAN[decade][j];
         int size = test.size();
         if ( s.size() >= size && s.substr( 0, size ) == test )
         {
            n += j * power;
            s.erase( 0, size );
            break;
         }
      }
   }

   if ( s != "" ) n = -1;   // ill-defined string

   return n;
}

//=====================================================================

int main()
{
   int n;
   string roman;

   cout << "Input a number (1-3999): ";   cin >> n;
   roman = intToRoman( n );
   if ( roman == "" ) cout << "Unable to convert\n";
   else               cout << roman << '\n';

   cout << "\n\n";

   cout << "Input a roman numeral: ";   cin >> roman;
   n = romanToInt( roman );
   if ( n <= 0 ) cout << "Unable to convert\n";
   else          cout << n << '\n';
}


Input a number (1-3999): 3848
MMMDCCCXLVIII


Input a roman numeral: MMMDCCCXLVIII
3848
@Mount eerie -- Kinda forgot about this thread, sorry ;P

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
#include <iostream>
#include <vector>
using namespace std;

vector<pair<string, int>> markers =
{
  {"M"  , 1000},
  {"CM" , 900},
  {"D"  , 500},
  {"CD" , 400},
  {"C"  , 100},
  {"XC" ,  90},
  {"L"  ,  50},
  {"XL" ,  40},
  {"X"  ,  10},
  {"IX" ,   9},
  {"V"  ,   5},
  {"IV" ,   4},
  {"I"  ,   1}
};

int from_roman(string s)
{
  int sum = 0;
  string single, twopart;

  for (int i=0; i<s.size(); ++i)
  {
    single = s.substr(i,1);
    twopart = s.substr(i,2);

    for (auto& mark : markers)
    {
      if (mark.first==single)
      {
        sum+=mark.second;
        break;
      }
      else if (mark.first == twopart)
      {
        sum+=mark.second;
        i++;
        break;
      }
    }
  }
  return sum;
}

int main()
{
  string years[] = {"MMMI", "MMMDCCCXLVIII", "LXXVIII", "MCMXCVIII", "DCC", "LXXXIII"};
  for (auto& y : years)
  {
    cout << from_roman(y) << "\n";
  }
  return 0;
}


For decoding, I define markers in descending order.
I take two slices of the string at position i -- single character and two characters.
If we're on the last character,
s.substr(i,2);
looks like it goes out of bounds, but shouldn't throw and return the same result as s.substr(1); . So we first check the single character and then the two-parter. As soon as we find it in markers, we increase the sum and break out, since there's no point iterating through the rest of markers. If we found a match with the two-character string, we increment the position in addition to what the for loop already increments.

Probably a little room for optimization, but should be straightforward/readable. Might return a sum of 0 or attempt to find roman tokens for strange input ("ZZZZCZZZZ" will probably return 100).

validation at this random site i found: https://www.rapidtables.com/math/symbols/roman_numerals.html

online c++ compile and run: https://repl.it/repls/WorriedProfitableDonateware
Topic archived. No new replies allowed.