Mocoso Joel Chippindale's occasional blog.

30 October 2010

Floating point arithmetic is going to kill you

We all know that floating point arithmetic is imprecise but most of us pretend this isn't a problem until it reaches out and bites us.

I was using Ruby Float to calculate VAT and, predictably, got bitten. The good news is that there is a simple solution.

The problem

The VAT rate in the UK is 17.5% (in Oct 2010) and so if we fire up the irb we can multiple by 0.175 (a Float in Ruby) to work out how much VAT to charge (in pence) on £105.00 as follows

> 10500 * 0.175
=> 1837.5

This all looks good, however we want to charge a whole number of pence and so let's do it again rounding the result to the nearest integer

> (10500 * 0.175).round
=> 1837

But wait 1837.5 should round up to 1838 shouldn't it?

> 10500 * 0.175
=> 1837.5
> (1837.5).round
=> 1838
> (10500 * 0.175).round
=> 1837

WTF! Lets look a little closer.

> 10500 * 0.175 == 1837.5
=> false
> sprintf('%.50f', 1837.5)
=> "1837.50000000000000000000000000000000000000000000000000"
> sprintf('%.50f', 10500 * 0.175)
=> "1837.49999999999977262632455676794052124023437500000000"

Aargh! So near and yet so far

The solution

Ruby has a neat solution for this.

Use a Rational instead of a Float to represent the percentage and get back to being precise.

> 10500 * Rational(175, 1000)
=> Rational(3675, 2)
> 10500 * Rational(175, 1000) == 1837.5
=> true
> sprintf('%.50f', 10500 * Rational(175, 1000))
=> "1837.50000000000000000000000000000000000000000000000000"
> (10500 * Rational(175, 1000)).round
=> 1838

Phew, all is well with the world again.

Now we can reliably use Ruby to calculate that 17.5% VAT on £105 is £18.38.

Thanks go to Tom for unravelling this knotted ball of string.