How can I convert an integer to its String representation in Roman numerals in C ?
Divide the given number into digits at different places like one's, two's, hundred's, or thousand's. Starting from the thousand's place print the corresponding roman value. For example, if the digit at thousand's place is 3 then print the roman equivalent of 3000. Repeat the second step until we reach one's place.
We take the corresponding roman value from the map and append it to our answer. At first, we process 6 thus ans = VI and our integer becomes 240. Second, we process 40 thus ans = XLVI and the integer become 200. Third, we process 200 and ans = CCXLVI and our loop terminates.
The easiest way is probably to set up three arrays for the complex cases and use a simple function like:
// convertToRoman: // In: val: value to convert. // res: buffer to hold result. // Out: n/a // Cav: caller responsible for buffer size. void convertToRoman (unsigned int val, char *res) { char *huns[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; char *tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; char *ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; int size[] = { 0, 1, 2, 3, 2, 1, 2, 3, 4, 2}; // Add 'M' until we drop below 1000. while (val >= 1000) { *res++ = 'M'; val -= 1000; } // Add each of the correct elements, adjusting as we go. strcpy (res, huns[val/100]); res += size[val/100]; val = val % 100; strcpy (res, tens[val/10]); res += size[val/10]; val = val % 10; strcpy (res, ones[val]); res += size[val]; // Finish string off. *res = '\0'; }
This will handle any unsigned integer although large numbers will have an awful lot of M
characters at the front and the caller has to ensure their buffer is large enough.
Once the number has been reduced below 1000, it's a simple 3-table lookup, one each for the hundreds, tens and units. For example, take the case where val
is 314
.
val/100
will be 3
in that case so the huns
array lookup will give CCC
, then val = val % 100
gives you 14
for the tens
lookup.
Then val/10
will be 1
in that case so the tens
array lookup will give X
, then val = val % 10
gives you 4
for the ones
lookup.
Then val
will be 4
in that case so the ones
array lookup will give IV
.
That gives you CCCXIV
for 314
.
A buffer-overflow-checking version is a simple step up from there:
// convertToRoman: // In: val: value to convert. // res: buffer to hold result. // Out: returns 0 if not enough space, else 1. // Cav: n/a int convertToRoman (unsigned int val, char *res, size_t sz) { char *huns[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; char *tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; char *ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; int size[] = { 0, 1, 2, 3, 2, 1, 2, 3, 4, 2}; // Add 'M' until we drop below 1000. while (val >= 1000) { if (sz-- < 1) return 0; *res++ = 'M'; val -= 1000; } // Add each of the correct elements, adjusting as we go. if (sz < size[val/100]) return 0; sz -= size[val/100]; strcpy (res, huns[val/100]); res += size[val/100]; val = val % 100; if (sz < size[val/10]) return 0; sz -= size[val/10]; strcpy (res, tens[val/10]); res += size[val/10]; val = val % 10; if (sz < size[val) return 0; sz -= size[val]; strcpy (res, ones[val]); res += size[val]; // Finish string off. if (sz < 1) return 0; *res = '\0'; return 1; }
although, at that point, you could think of refactoring the processing of hundreds, tens and units into a separate function since they're so similar. I'll leave that as an extra exercise.
don't use a sissy pre-calculated map for the difficult cases.
/* roman.c */ #include <stdio.h> /* LH(1) roman numeral conversion */ int RN_LH1 (char *buf, const size_t maxlen, int n) { int S[] = { 0, 2, 4, 2, 4, 2, 4 }; int D[] = { 1000, 500, 100, 50, 10, 5, 1 }; char C[] = { 'M', 'D', 'C', 'L', 'X', 'V', 'I' }; const size_t L = sizeof(D) / sizeof(int) - 1; size_t k = 0; /* index into output buffer */ int i = 0; /* index into maps */ int r, r2; while (n > 0) { if (D[i] <= n) { r = n / D[i]; n = n - (r * D[i]); /* lookahead */ r2 = n / D[i+1]; if (i < L && r2 >= S[i+1]) { /* will violate repeat boundary on next pass */ n = n - (r2 * D[i+1]); if (k < maxlen) buf[k++] = C[i+1]; if (k < maxlen) buf[k++] = C[i-1]; } else if (S[i] && r >= S[i]) { /* violated repeat boundary on this pass */ if (k < maxlen) buf[k++] = C[i]; if (k < maxlen) buf[k++] = C[i-1]; } else while (r-- > 0 && k < maxlen) buf[k++] = C[i]; } i++; } if (k < maxlen) buf[k] = '\0'; return k; } /* gcc -Wall -ansi roman.c */ int main (int argc, char **argv) { char buf[1024] = {'\0'}; size_t len; int k; for (k = 1991; k < 2047; k++) { len = RN_LH1(buf, 1023, k); printf("%3lu % 4d %s\n", len, k, buf); } return 0; }
you don't actually need to declare S
either. it should be easy to see why.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With