I have a numeric value like 30.6355 that represents money, how to round to 2 decimal places?
Asked
Active
Viewed 5,768 times
4
-
2Are you selling gasoline? Why would you have money out to the 5th decimal point? – Jason Whitehorn Nov 09 '10 at 04:22
-
1possible 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
-
2Oh, 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
-
1After computing tax or discounts, `>2` decimals are quite common... – Michael Haren Nov 09 '10 at 04:36
3 Answers
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
-
8Actually, the biggest problem is that they don't have any decimal places *at all*, they have *binary* places. – Jörg W Mittag Nov 09 '10 at 05:09
-
@Jörg, is right. See http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems for an explanation. – lucasrizoli Nov 09 '10 at 05:59
-
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