Add 10 percent to a number… What can possibly go wrong!?
One of the recruitment stages at Shutl is a pair programming exercise where candidates pair with one of our developers on adding couple of features to a small web service. One of the features includes adding a markup to the base price.
Problem
We have a base price(in pence) expressed as integer. Add 10% markup to the base price and express the final price(in pence) as an integer. It’s up to you to represent the markup and you can choose rounding or flooring when converting back to an integer.
Example:
316 + 10% of 316 = 348 (or 347 if you floor it instead of rounding)
We used to do the exercise in Ruby but lately our team needs more Java expertise so we ported it over and it seems to have caused more problems in the statically typed Java than it did in Ruby.
Everyone I did the exercise with was able to do the above calculation on a piece of paper or using google calculator. Programming a computer to calculate it seems to be a bit harder though.
Some candidates just glide through the calculation without even noticing it (that’s good), some get stuck for a moment whereas others get really stuck until the end of the exercise.
The problem is so rudimentary that normally I refrain from guiding the candidate. They can do trial and error until they get code that compiles and returns the correct value or they can try googling the solution. Some find the way out, some get stuck for good.
So let’s try to add 10% to the price and see what can possibly go wrong.
Ruby
Markup as float multiplier
base_price = 316 multiplier = 1.1 final_price = (base_price*multiplier).round
Final price is 348 — not much can go wrong.
Markup as integer percentage
base_price = 316 markup = 10 final_price = base_price + (markup / 100) * base_price
That doesn’t work – final price is 316. Because of integer division; lets try it in irb
:
>> 10 / 100 => 0
This gives better results:
>> 10.to_f / 100 => 0.1
Final calculation:
final_price = (base_price + (markup.to_f / 100) * base_price).round
gives us the expected 348. Inner parentheses are not necessary but some people prefer to keep them.
As we don’t mind the result to be floored instead of rounded it’s possible to get correct result using integer division:
final_price = base_price + base_price * markup / 100
The result is 347 with a clean and simple formulae.
Java
Markup as a float multiplier
We start with the definitions:
Long basePrice = 316L; double multiplier = 1.1;
where we use boxed Long
for basePrice
– this is dictated by the context of the computation and we want to keep types as they are.
First attempt:
Long finalPrice = basePrice * multiplier;
does not compile with error:
Type mismatch: cannot convert from double to Long
Ruby was easier in this case than Java. If we use Eclipse’s quick fix casting the boxed type to a primitivelong
we get
Long finalPrice = (long) (basePrice * multiplier);
with the correct value of 347. Looks very simple but there are many things happening here:
basePrice
is unboxed fromLong
tolong
basePrice
long
is then converted todouble
(widening primitive conversion)basePrice
double
is multiplied by multiplier (alsodouble
) and the result is adouble
- resulting
double
is cast tolong
with (narrowing primitive conversion) - resulting
long
is autoboxed toLong
(assignment conversion)
If we try to do above conversions manually it could look like this:
Long.valueOf( (long) ( (double) basePrice.longValue() * multiplier));
Details of the conversions and promotion rules can be found in Java documentation.
It’s good to understand the difference between integer and float but we can get away without understanding all the details of the above conversions. In this case the IDE fixed the problem for us.
If we prefer rounding to flooring we can simply do:
Long finalPrice = Math.round(basePrice*multiplier);
Markup as an integer percentage
Long basePrice = 316L; int markup = 10;
As with Ruby, this will not work:
Long finalPrice = basePrice + (markup/100)*basePrice;
resulting in 316. Again this is because of integer division 10/100 resulting in 0. We can get this formula to work by changing type of the divisor(100) to double
and casting the result of the computation to a long
:
Long finalPrice = (long) (basePrice + (markup/100d)*basePrice);
It results in 347. If we are not happy with flooring the result we can use Math.round
:
Long finalPrice = Math.round(basePrice + (markup/100d)*basePrice);
As with the Ruby example, changing the order of the formula allows us to use integer division:
Long finalPrice = basePrice + markup*basePrice/100;
Conclusion
- Interviews can be a little stressful and simple problems can turn into big obstacles
- It’s good to understand the difference between integer and float
- Let the IDE fix the problem and rerun your tests
Now you’re ready for our pair programming exercise.