21

How do you find a roman numeral equivalent of an integer. Is there a java library which provides this capability?

I did find a similar question, but I would prefer an out of the box API abstraction for this issue. Its just painful to handle all possible combinations in your code.

Community
  • 1
  • 1
Jason
  • 12,229
  • 20
  • 51
  • 66
  • 3
    There are hundreds of code samples for that on the internet. Why do you ask for that in a forum instead of using google? – codymanix Oct 13 '10 at 08:43
  • 5
    It isn't painful, it is a very good introduction to Test Driven Development. – Thorbjørn Ravn Andersen Oct 13 '10 at 09:07
  • 4
    Code samples, yes. Standard libraries, no. The question is perfectly valid. – Sean Patrick Floyd Oct 13 '10 at 09:09
  • If you are just looking for a library, then this has nothing to do with math. – aaronasterling Oct 13 '10 at 09:14
  • I guess nobody bothers to create a library **because** there are so many code samples. Given the choice between adding another dependency and just copy-pasting some code, most people would probably choose the second. – Cephalopod Oct 13 '10 at 09:25
  • My guess is that nobody bothers because ... hardly any *real* applications need to do roman number conversion. Only homework questions, lame job interview questions and websites whose real purpose is to attract clicks for advertising revenue. (And the latter are probably coded in PHP ...) – Stephen C Dec 05 '21 at 08:02

4 Answers4

13

Here is a link for many languages including Java. Here's an extract of relevance:

public class RN {

    enum Numeral {
        I(1), IV(4), V(5), IX(9), X(10), XL(40), L(50), XC(90), C(100), CD(400), D(500), CM(900), M(1000);
        int weight;

        Numeral(int weight) {
            this.weight = weight;
        }
    };

    public static String roman(long n) {

        if( n <= 0) {
            throw new IllegalArgumentException();
        }

        StringBuilder buf = new StringBuilder();

        final Numeral[] values = Numeral.values();
        for (int i = values.length - 1; i >= 0; i--) {
            while (n >= values[i].weight) {
                buf.append(values[i]);
                n -= values[i].weight;
            }
        }
        return buf.toString();
    }

    public static void test(long n) {
        System.out.println(n + " = " + roman(n));
    }

    public static void main(String[] args) {
        test(1999);
        test(25);
        test(944);
        test(0);
    }

}
tbodt
  • 16,609
  • 6
  • 58
  • 83
Petar Minchev
  • 46,889
  • 11
  • 103
  • 119
9

This is the code I am using, right next to the excel column name converter. Why isnt there an apache library for this stuff?

private static final char[] R = {'ↂ', 'ↁ', 'M', 'D', 'C', 'L', 'X', 'V', 'I'};
// or, as suggested by Andrei Fierbinteanu
// private static final String[] R = {"X\u0305", "V\u0305", "M", "D", "C", "L", "X", "V", "I"};
private static final int MAX = 10000; // value of R[0], must be a power of 10

private static final int[][] DIGITS = {
    {},{0},{0,0},{0,0,0},{0,1},{1},
    {1,0},{1,0,0},{1,0,0,0},{0,2}};

public static String int2roman(int number) {
    if (number < 0 || number >= MAX*4) throw new IllegalArgumentException(
            "int2roman: " + number + " is not between 0 and " + (MAX*4-1));
    if (number == 0) return "N";
    StringBuilder sb = new StringBuilder();
    int i = 0, m = MAX;
    while (number > 0) {
        int[] d = DIGITS[number / m];
        for (int n: d) sb.append(R[i-n]);
        number %= m;
        m /= 10;
        i += 2;
    }
    return sb.toString();
}

Edit:

Now that I look at it again, the loop can be condensed to

    for (int i = 0, m = MAX; m > 0; m /= 10, i += 2) {
        int[] d = DIGITS[(number/m)%10];
        for (int n: d) sb.append(R[i-n]);
    }

making the code even less readable ;-)

Cephalopod
  • 14,632
  • 7
  • 51
  • 70
3

This my answer:

Use this libreries...

import java.util.LinkedHashMap;
import java.util.Map;

The code

  public static String RomanNumerals(int Int) {
    LinkedHashMap<String, Integer> roman_numerals = new LinkedHashMap<String, Integer>();
    roman_numerals.put("M", 1000);
    roman_numerals.put("CM", 900);
    roman_numerals.put("D", 500);
    roman_numerals.put("CD", 400);
    roman_numerals.put("C", 100);
    roman_numerals.put("XC", 90);
    roman_numerals.put("L", 50);
    roman_numerals.put("XL", 40);
    roman_numerals.put("X", 10);
    roman_numerals.put("IX", 9);
    roman_numerals.put("V", 5);
    roman_numerals.put("IV", 4);
    roman_numerals.put("I", 1);
    String res = "";
    for(Map.Entry<String, Integer> entry : roman_numerals.entrySet()){
      int matches = Int/entry.getValue();
      res += repeat(entry.getKey(), matches);
      Int = Int % entry.getValue();
    }
    return res;
  }
  public static String repeat(String s, int n) {
    if(s == null) {
        return null;
    }
    final StringBuilder sb = new StringBuilder();
    for(int i = 0; i < n; i++) {
        sb.append(s);
    }
    return sb.toString();
  }

Testing the code

  for (int i = 1;i<256;i++) {
    System.out.println("i="+i+" -> "+RomanNumerals(i));
  }

The output:

  i=1 -> I
  i=2 -> II
  i=3 -> III
  i=4 -> IV
  i=5 -> V
  i=6 -> VI
  i=7 -> VII
  i=8 -> VIII
  i=9 -> IX
  i=10 -> X
  i=11 -> XI
  i=12 -> XII
  i=13 -> XIII
  i=14 -> XIV
  i=15 -> XV
  i=16 -> XVI
  i=17 -> XVII
  i=18 -> XVIII
  i=19 -> XIX
  i=20 -> XX
  i=21 -> XXI
  i=22 -> XXII
  i=23 -> XXIII
  i=24 -> XXIV
  i=25 -> XXV
  i=26 -> XXVI
  i=27 -> XXVII
  i=28 -> XXVIII
  i=29 -> XXIX
  i=30 -> XXX
  i=31 -> XXXI
  i=32 -> XXXII
  i=33 -> XXXIII
  i=34 -> XXXIV
  i=35 -> XXXV
  i=36 -> XXXVI
  i=37 -> XXXVII
  i=38 -> XXXVIII
  i=39 -> XXXIX
  i=40 -> XL
  i=41 -> XLI
  i=42 -> XLII
  i=43 -> XLIII
  i=44 -> XLIV
  i=45 -> XLV
  i=46 -> XLVI
  i=47 -> XLVII
  i=48 -> XLVIII
  i=49 -> XLIX
  i=50 -> L
  i=51 -> LI
  i=52 -> LII
  i=53 -> LIII
  i=54 -> LIV
  i=55 -> LV
  i=56 -> LVI
  i=57 -> LVII
  i=58 -> LVIII
  i=59 -> LIX
  i=60 -> LX
  i=61 -> LXI
  i=62 -> LXII
  i=63 -> LXIII
  i=64 -> LXIV
  i=65 -> LXV
  i=66 -> LXVI
  i=67 -> LXVII
  i=68 -> LXVIII
  i=69 -> LXIX
  i=70 -> LXX
  i=71 -> LXXI
  i=72 -> LXXII
  i=73 -> LXXIII
  i=74 -> LXXIV
  i=75 -> LXXV
  i=76 -> LXXVI
  i=77 -> LXXVII
  i=78 -> LXXVIII
  i=79 -> LXXIX
  i=80 -> LXXX
  i=81 -> LXXXI
  i=82 -> LXXXII
  i=83 -> LXXXIII
  i=84 -> LXXXIV
  i=85 -> LXXXV
  i=86 -> LXXXVI
  i=87 -> LXXXVII
  i=88 -> LXXXVIII
  i=89 -> LXXXIX
  i=90 -> XC
  i=91 -> XCI
  i=92 -> XCII
  i=93 -> XCIII
  i=94 -> XCIV
  i=95 -> XCV
  i=96 -> XCVI
  i=97 -> XCVII
  i=98 -> XCVIII
  i=99 -> XCIX
  i=100 -> C
  i=101 -> CI
  i=102 -> CII
  i=103 -> CIII
  i=104 -> CIV
  i=105 -> CV
  i=106 -> CVI
  i=107 -> CVII
  i=108 -> CVIII
  i=109 -> CIX
  i=110 -> CX
  i=111 -> CXI
  i=112 -> CXII
  i=113 -> CXIII
  i=114 -> CXIV
  i=115 -> CXV
  i=116 -> CXVI
  i=117 -> CXVII
  i=118 -> CXVIII
  i=119 -> CXIX
  i=120 -> CXX
  i=121 -> CXXI
  i=122 -> CXXII
  i=123 -> CXXIII
  i=124 -> CXXIV
  i=125 -> CXXV
  i=126 -> CXXVI
  i=127 -> CXXVII
  i=128 -> CXXVIII
  i=129 -> CXXIX
  i=130 -> CXXX
  i=131 -> CXXXI
  i=132 -> CXXXII
  i=133 -> CXXXIII
  i=134 -> CXXXIV
  i=135 -> CXXXV
  i=136 -> CXXXVI
  i=137 -> CXXXVII
  i=138 -> CXXXVIII
  i=139 -> CXXXIX
  i=140 -> CXL
  i=141 -> CXLI
  i=142 -> CXLII
  i=143 -> CXLIII
  i=144 -> CXLIV
  i=145 -> CXLV
  i=146 -> CXLVI
  i=147 -> CXLVII
  i=148 -> CXLVIII
  i=149 -> CXLIX
  i=150 -> CL
  i=151 -> CLI
  i=152 -> CLII
  i=153 -> CLIII
  i=154 -> CLIV
  i=155 -> CLV
  i=156 -> CLVI
  i=157 -> CLVII
  i=158 -> CLVIII
  i=159 -> CLIX
  i=160 -> CLX
  i=161 -> CLXI
  i=162 -> CLXII
  i=163 -> CLXIII
  i=164 -> CLXIV
  i=165 -> CLXV
  i=166 -> CLXVI
  i=167 -> CLXVII
  i=168 -> CLXVIII
  i=169 -> CLXIX
  i=170 -> CLXX
  i=171 -> CLXXI
  i=172 -> CLXXII
  i=173 -> CLXXIII
  i=174 -> CLXXIV
  i=175 -> CLXXV
  i=176 -> CLXXVI
  i=177 -> CLXXVII
  i=178 -> CLXXVIII
  i=179 -> CLXXIX
  i=180 -> CLXXX
  i=181 -> CLXXXI
  i=182 -> CLXXXII
  i=183 -> CLXXXIII
  i=184 -> CLXXXIV
  i=185 -> CLXXXV
  i=186 -> CLXXXVI
  i=187 -> CLXXXVII
  i=188 -> CLXXXVIII
  i=189 -> CLXXXIX
  i=190 -> CXC
  i=191 -> CXCI
  i=192 -> CXCII
  i=193 -> CXCIII
  i=194 -> CXCIV
  i=195 -> CXCV
  i=196 -> CXCVI
  i=197 -> CXCVII
  i=198 -> CXCVIII
  i=199 -> CXCIX
  i=200 -> CC
  i=201 -> CCI
  i=202 -> CCII
  i=203 -> CCIII
  i=204 -> CCIV
  i=205 -> CCV
  i=206 -> CCVI
  i=207 -> CCVII
  i=208 -> CCVIII
  i=209 -> CCIX
  i=210 -> CCX
  i=211 -> CCXI
  i=212 -> CCXII
  i=213 -> CCXIII
  i=214 -> CCXIV
  i=215 -> CCXV
  i=216 -> CCXVI
  i=217 -> CCXVII
  i=218 -> CCXVIII
  i=219 -> CCXIX
  i=220 -> CCXX
  i=221 -> CCXXI
  i=222 -> CCXXII
  i=223 -> CCXXIII
  i=224 -> CCXXIV
  i=225 -> CCXXV
  i=226 -> CCXXVI
  i=227 -> CCXXVII
  i=228 -> CCXXVIII
  i=229 -> CCXXIX
  i=230 -> CCXXX
  i=231 -> CCXXXI
  i=232 -> CCXXXII
  i=233 -> CCXXXIII
  i=234 -> CCXXXIV
  i=235 -> CCXXXV
  i=236 -> CCXXXVI
  i=237 -> CCXXXVII
  i=238 -> CCXXXVIII
  i=239 -> CCXXXIX
  i=240 -> CCXL
  i=241 -> CCXLI
  i=242 -> CCXLII
  i=243 -> CCXLIII
  i=244 -> CCXLIV
  i=245 -> CCXLV
  i=246 -> CCXLVI
  i=247 -> CCXLVII
  i=248 -> CCXLVIII
  i=249 -> CCXLIX
  i=250 -> CCL
  i=251 -> CCLI
  i=252 -> CCLII
  i=253 -> CCLIII
  i=254 -> CCLIV
  i=255 -> CCLV

Best Regards

joseluisbz
  • 1,491
  • 1
  • 36
  • 58
0

My version uses unmodified lookup and is more explicit.

 public String numToRoman(int num) {
    int numLength = (int) (Math.log10(num) + 1);
    int place_value = 10;
    final Map<Integer, char[]> romanModeLookup = new HashMap<>();
    romanModeLookup.putAll(Map.of(10, new char[]{'I', 'V', 'X'}, 100, new char[]{'X', 'L', 'C'},
            1000, new char[]{'C', 'D', 'M'}, 10000, new char[]{'M'}));


    final StringBuilder romanBuilder = new StringBuilder();
    while (numLength > 0) {
        numLength--;
        int current_num_at_place_val = (num % place_value) / (place_value / 10);
        System.out.println(current_num_at_place_val);

        if (place_value < 10000) {
            if (current_num_at_place_val == 9) {
                romanBuilder.append(romanModeLookup.get(place_value)[2]).append(romanModeLookup.get(place_value)[0]);
            } else if (current_num_at_place_val >= 5) {
                int remaining_sticks = current_num_at_place_val - 5;
                while (remaining_sticks > 0) {
                    romanBuilder.append(romanModeLookup.get(place_value)[0]);
                    remaining_sticks--;
                }
                romanBuilder.append(romanModeLookup.get(place_value)[1]);

            } else if (current_num_at_place_val == 4) {
                romanBuilder.append(romanModeLookup.get(place_value)[1]).append(romanModeLookup.get(place_value)[0]);
            } else {
                while (current_num_at_place_val > 0) {
                    romanBuilder.append(romanModeLookup.get(place_value)[0]);
                    current_num_at_place_val--;
                }
            }
        } else if (place_value == 10000) {
            while (current_num_at_place_val > 0) {
                romanBuilder.append(romanModeLookup.get(10000)[0]);
                current_num_at_place_val--;
            }
        } else {
            throw new IllegalArgumentException("Number too big for Romanization");
        }
        place_value *= 10;
    }

    return romanBuilder.reverse().toString();}
douglas
  • 116
  • 3