Roman numerals to decimal conversion is a classic algorithm problem that tests your ability to identify the subtractive notation rule. Roman numerals use seven symbols (I, V, X, L, C, D, M) and a key rule: when a smaller value symbol appears before a larger one, it is subtracted instead of added. So IV = 5 − 1 = 4, and IX = 10 − 1 = 9.
The original version of this program used uninitialized pointers (int *a and char *rom with no allocated memory), conio.h, and void main() — it would segfault immediately on any modern system. This rewrite is clean, safe, and compiles with zero warnings under gcc -ansi -Wall -Wextra.
Roman Numeral Symbol Values
| Symbol | I | V | X | L | C | D | M |
|---|---|---|---|---|---|---|---|
| Value | 1 | 5 | 10 | 50 | 100 | 500 | 1000 |
The Subtractive Rule
Scan the string from left to right. For each symbol:
- If the next symbol has a larger value — subtract the current symbol.
- Otherwise — add the current symbol.
Example: MCMXCIX → M + (−C+M) + (−X+C) + (−I+X) = 1000 + 900 + 90 + 9 = 1999
C Program to Convert Roman Numeral to Decimal
/* Convert Roman numeral to decimal
* Rule: if current symbol < next symbol, subtract it; otherwise add it.
* Compile: gcc -ansi -Wall -Wextra roman.c -o roman */
#include <stdio.h>
#include <string.h>
int roman_value(char c)
{
switch (c) {
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
default: return -1;
}
}
int roman_to_decimal(const char *roman)
{
int result = 0;
int i, cur, next;
int len = (int)strlen(roman);
for (i = 0; i < len; i++) {
cur = roman_value(roman[i]);
if (cur == -1)
return -1; /* invalid character */
if (i + 1 < len) {
next = roman_value(roman[i + 1]);
if (next > cur)
result -= cur; /* subtractive: IV=4, IX=9, XL=40, etc. */
else
result += cur;
} else {
result += cur; /* last symbol always added */
}
}
return result;
}
int main(void)
{
char roman[50];
int decimal;
printf("Enter a Roman numeral: ");
scanf("%49s", roman);
decimal = roman_to_decimal(roman);
if (decimal == -1)
printf("Error: invalid Roman numeral character in \"%s\".\n", roman);
else
printf("%s = %d\n", roman, decimal);
return 0;
}
How to Compile and Run
gcc -ansi -Wall -Wextra roman.c -o roman
./roman
Sample Output
Enter a Roman numeral: XIV XIV = 14 Enter a Roman numeral: IV IV = 4 Enter a Roman numeral: MCMXCIX MCMXCIX = 1999 Enter a Roman numeral: MMXXIV MMXXIV = 2024 Enter a Roman numeral: XYZ Error: invalid Roman numeral character in "XYZ".
Step-by-Step Trace: MCMXCIX = 1999
| i | roman[i] | cur | next | Action | result |
|---|---|---|---|---|---|
| 0 | M | 1000 | C=100 | next < cur → add 1000 | 1000 |
| 1 | C | 100 | M=1000 | next > cur → subtract 100 | 900 |
| 2 | M | 1000 | X=10 | next < cur → add 1000 | 1900 |
| 3 | X | 10 | C=100 | next > cur → subtract 10 | 1890 |
| 4 | C | 100 | I=1 | next < cur → add 100 | 1990 |
| 5 | I | 1 | X=10 | next > cur → subtract 1 | 1989 |
| 6 | X | 10 | — | last symbol → add 10 | 1999 |
Common Roman Numeral Equivalents
| Roman | Decimal | Roman | Decimal | Roman | Decimal |
|---|---|---|---|---|---|
| I | 1 | X | 10 | C | 100 |
| IV | 4 | XL | 40 | CD | 400 |
| V | 5 | L | 50 | D | 500 |
| IX | 9 | XC | 90 | CM | 900 |
| XI | 11 | LX | 60 | M | 1000 |
| XIV | 14 | XCI | 91 | MCMXCIX | 1999 |
Code Explanation
- roman_value() returns −1 for invalid characters — the caller checks for −1 immediately in the loop, allowing clean error reporting. In the original code, unknown characters would silently store garbage into an uninitialized int pointer.
- The subtractive rule in one if-statement —
if (next > cur) result -= cur; else result += cur;. Only six valid subtractive combinations exist in standard Roman numerals: IV, IX, XL, XC, CD, CM. The algorithm handles all of them without special-casing each pair. - Last symbol: no next to compare — the condition
i + 1 < lenguards theroman[i+1]access. The last symbol always contributes positively — the loop’s else branch handles it. - scanf(“%49s”, roman) — limits input to 49 characters plus the null terminator, preventing buffer overflow. The original used
scanf("%s", rom)on an uninitialized pointer — a guaranteed crash or undefined behavior. - const char *roman in roman_to_decimal() — the function does not modify the string, so the parameter is const-qualified. This allows passing string literals safely and documents the intent.
What This Program Teaches
- Left-to-right scanning with lookahead — the algorithm peeks at the next element (
roman[i+1]) without advancing i, then decides whether to add or subtract the current one. This lookahead pattern appears in lexers, parsers, and many string algorithms. - Switch for character-to-value mapping — a switch statement is faster than a chain of if-else when comparing one variable against a set of constants. The compiler can generate a jump table.
- Initializing vs. allocating pointers — the original code had
int *awith no malloc — a dangling pointer. Arrays with fixed bounds (int a[50]orchar roman[50]) are the safe choice when the maximum size is known at compile time. - Returning error codes from string functions —
roman_to_decimal()returns −1 for invalid input instead of printing an error message itself. Separating parsing from reporting makes the function reusable in larger programs.
Related Programs
- Binomial Coefficients in C
- Combinations and Permutations in C
- GCD and LCM using Recursion in C
- sizeof Data Types in C
- All Data Types in C
Recommended book:
The C Programming Language — Kernighan & Ritchie (India) |
(US)
|
C Programming: A Modern Approach — K.N. King (India) |
(US)
Practice what you learned: C Aptitude Questions — or try our C Programming Quiz App on Android.