Adding, the old-fashioned way

Python, 100

s="IVXLCDM"
r=raw_input()
a=""
i=2
u=0
for c in s:r+=u/i*c;i=7-i;u=r.count(c);a+=u%i*c
print a[::-1]

Takes one string from input (e.g. VIII + XII or VIII + XII =).


APL (59 56)

,/N⍴⍨¨{∨/K←⍵≥D←7⍴5 2:∇⍵+(1⌽K)-K×D⋄⍵}⊃+/(N←'MDCLXVI')∘=¨⍞

Input on one line (i.e. XII + XII, though the + is not necessary).

Edit: changed shift to rotate to save three characters - it only matters when the answer ≥ 5000, which should never happen as the question says the input values will always be ≤ 2000. The only effect is it now "overflows" at 5000, giving 5000=1, 5001=2, etc.

(I don't really think the Romans did it this way... APL is more something for Ancient Egyptians I think :) )

Explanation:

  • : get user input
  • (N←'MDCLXVI')∘=¨: store 'MDCLXVI' in N. Return, for each character of the input string, a vector with a 1 in the place where the character corresponds to one of 'MDCLXVI', and 0 otherwise.
  • ⊃+/: Sum the vectors and de-encapsulate. We now have a vector with information about how many of each Roman numeral we have. I.e, if the input was XXII XIIII, we now have:
     M D C L X V I
     0 0 0 0 3 0 6
  • Note that this is not converting the values.
  • {...:......} is a function with an if-else construct.
  • D←7⍴5 2: D is the vector 5 2 5 2 5 2 5. This is how many of a Roman numeral are not allowed. I.e. if you have 5 Is, that's too many, and if you have 2 Vs that's also too many. This vector also happens to be the multiplication factor for each Roman numeral, i.e. a V is worth 5 Is and an X is worth 2 Vs.

  • ∨/K←⍵≥D: K is the vector where there's a 1 if we have too many Roman numerals of a certain kind. ∨/ ORs this vector together.

  • If this vector is not all zeroes:
  • K×D: Multiply K by D. This vector has zeroes where we don't have too many Roman numerals, and the amount of Roman numerals where we do.
  • ⍵+(1⌽K): Rotate K to the left by 1, and add it to the input. For each Roman numeral we have too many, this will add one of the next-higher one.
  • ⍵+(1⌽K)-K×D: Subtract this from the other vector. The effect is, for example if you have 6 Is, it will add one V and remove 4 Is.
  • : Recurse.
  • ⋄⍵: But if K was all zeroes, then ⍵ represents a valid Roman numeral, so return ⍵.
  • N⍴⍨¨: For each element of the resulting vector, create that many of the corresponding Roman numeral.
  • ,/: Join these vectors together to get rid of the ugly spaces in the output.

Ruby, 85 82 characters

gets;r=0;v=5;puts"IVXLCDM".gsub(/./){|g|r+=$_.count g;t=r%v;r/=v;v^=7;g*t}.reverse

This version takes input on STDIN as a single string (e.g. XXIIII + XXXXII) and prints output to STDOUT.

f=->*a{r=0;v=5;"IVXLCDM".gsub(/./){|g|r+=(a*'').count g;t=r%v;r/=v;v^=7;g*t}.reverse}

The second one is an implementation as a function. Takes two (or more) strings and returns the summed values. Usage:

puts f["XXIIII", "XXXXII"]     # -> LXVI