4

I have a numeric value like 30.6355 that represents money, how to round to 2 decimal places?

Mike Trpcic
  • 24,677
  • 8
  • 74
  • 112
Blankman
  • 248,432
  • 309
  • 736
  • 1,161
  • 2
    Are you selling gasoline? Why would you have money out to the 5th decimal point? – Jason Whitehorn Nov 09 '10 at 04:22
  • 1
    possible duplicate of [Round a ruby integer up to the nearest 0.05](http://stackoverflow.com/questions/1346257/round-a-ruby-integer-up-to-the-nearest-0-05) - question says 0.05 but the same technique applies to any unit, such as 0.01 – Greg Hewgill Nov 09 '10 at 04:24
  • 2
    Oh, money in float... again... Is `.0055` an accumulated float error? ))) – Nakilon Nov 09 '10 at 04:27
  • @Jason Because many people that deal with money (need to) keep values much more precise than the average consumer...? – deceze Nov 09 '10 at 04:27
  • 1
    After computing tax or discounts, `>2` decimals are quite common... – Michael Haren Nov 09 '10 at 04:36

3 Answers3

20

You should not use double or float types when dealing with currency: they have both too many decimal places and occasional rounding errors. Money can fall through those holes and it'll be tough to track down the errors after it happens.

When dealing with money, use a fixed decimal type. In Ruby (and Java), use BigDecimal.

lucasrizoli
  • 628
  • 5
  • 14
13

Ruby 1.8:

class Numeric
    def round_to( places )
        power = 10.0**places
        (self * power).round / power
    end
end

(30.6355).round_to(2)

Ruby 1.9:

(30.6355).round(2)

In 1.9, round can round to a specified number of digits.

Jason Gilmore
  • 3,102
  • 3
  • 22
  • 28
Reese Moore
  • 11,358
  • 3
  • 23
  • 32
0

This will round for some useful cases - not well written but it works! Feel free to edit.

def round(numberString)
numberString = numberString.to_s
decimalLocation = numberString.index(".")
numbersAfterDecimal = numberString.slice(decimalLocation+1,numberString.length-1)
numbersBeforeAndIncludingDeciaml = numberString.slice(0,decimalLocation+1)

if numbersAfterDecimal.length <= 2
    return numberString.to_f
end

thingArray = numberString.split("")
thingArray.pop

prior = numbersAfterDecimal[-1].to_i
idx = numbersAfterDecimal.length-2

thingArray.reverse_each do |numStr|
    if prior >= 5
        numbersAfterDecimal[idx] = (numStr.to_i + 1).to_s unless (idx == 1 && numStr.to_i == 9)
        prior = (numStr.to_i + 1)
    else
        prior = numStr.to_i
    end
    break if (idx == 1)
    idx -= 1
end

resp = numbersBeforeAndIncludingDeciaml + numbersAfterDecimal[0..1]
resp.to_f
end

round(18.00) == 18.0

round(18.99) == 18.99

round(17.9555555555) == 17.96

round(17.944444444445) == 17.95

round(15.545) == 15.55

round(15.55) == 15.55

round(15.555) == 15.56

round(1.18) == 1.18

round(1.189) == 1.19

The Dembinski
  • 1,410
  • 14
  • 23