eBay Tech London

November 23, 2016 Shutl, Tech

Add 10 percent to a number… What can possibly go wrong!?

By:

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 from Long to long
  • basePrice long is then converted to double (widening primitive conversion)
  • basePrice double is multiplied by multiplier (also double) and the result is a double
  • resulting double is cast to long with (narrowing primitive conversion)
  • resulting long is autoboxed to Long (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.

Leave a Reply

Your e-mail address will not be published. Required fields are marked *