TWIL - rounding to cents is not that easy
I had to do some floating point math with money and wanted values to round to two decimal places. I was using ruby 2.1.5, which doesn’t have the nice rounding functions in ruby 2.4 yet… so I had to roll my own.
My own round_cents
I ended up using this simple method to round values to two decimal places:
1
2
3
def round_cents(value)
(value * 100).to_i / 100.0
end
Having the .to_i made a difference. (value * 100) / 100.0
basically returns the same value. I want to only have the last cent,
no more, no less.
2.1.5 :001 > (1.2345 * 100).to_i / 100.0
=> 1.23
2.1.5 :002 > (1.2345 * 100) / 100.0
=> 1.2345 So, I added in this function to round everything as needed. Made sure tests were using it as well. Test suite passes, pull request made. No problems, right?
Random errors in test suite…
Then the test suite raises an error around one of the specs I checked in… huh??
I run the spec myself, no problems… I run the specs for the model… no problems. Ok, I’ll run the whole suite myself. No problems.
Kept digging
I kept digging further. It’s definitely not a race condition. Our suite uses random dollar amounts in making an order. Good practice. I started printing those out and basically saw:
puts round_cents(19.99) # => 19.98
(19.99 * 100).to_i / 100.0 # => 19.98HUH!? How did that happen?? Is this happening to other values??
Well, long story short: it does happen to other values, but not all *.99 values, which is weird. (Can you guess which other ones? Tweet me, @redgreenrepeat, your answers if you have run into the same problem.)
.to_d to the rescue
The fix: use ruby’s .to_d method on the value before working with
it. Now the round_cents method is:
1
2
3
def round_cents(value)
(value.to_d * 100).to_i / 100.0
end
and now…
puts round_cents(19.99) # => 19.99and I sleep better at night, not worried about the server being off in rounding cents…
tl;dr: I learn to use ruby’s .to_d on a value to give operations
extra precision.