Convert cents into dollar string in Ruby without use of BigDecimal

11,226

Solution 1

Here's a one-line method that also simply uses string manipulation thereby completely bypassing the numeric issues:

cents.rjust(3, "0").insert(-3, ".")

Solution 2

As Micheal Kohl already answered: Take a look to the money gem.

Example:

require 'money'
Money.use_i18n = false  #https://stackoverflow.com/q/31133229/676874
puts Money.new( 99, 'USD')
puts Money.new(324, 'USD')

The following seems to work, but is it correct?

(cents.to_i/100.0).to_s

On the first look, it is ok, but:

cents = '10'
p (cents.to_i/100.0).to_s # -> '0.1'

You don't have 2 digits.

Alternative:

p '%.2f' % (cents.to_i/100.0) # -> '0.10'

Solution 3

If they're stings already you could use string manipulation and bypass the numeric problems completely:

# There are, of course, all sorts of ways to do this.
def add_decimal(s)
  pfx = [ '0.00', '0.0', '0.' ]
  if(pfx[s.length])
    s = pfx[s.length] + s
  else
    s = s.dup
    s[-2, 0] = '.'
  end
  s
end

add_decimal('')      #   "0.00" 
add_decimal('1')     #   "0.01" 
add_decimal('12')    #   "0.12" 
add_decimal('123')   #   "1.23" 
add_decimal('1234')  #  "12.34" 
add_decimal('12345') # "123.45"

No precision issues, no floating point, no bignums, no Rational, nothing tricky, nothing clever. Some simple modifications would be needed to deal with negative values but that will be as simple as what's already there.

Solution 4

You can consider using Rationals as well. However, I am not sure do they get converted to floats when sprintf-ed:

"%.2f" % Rational("324".to_i,100)
#=> "3.24"
"%.2f" % Rational("99".to_i,100)
#=> "0.99"
"%.2f" % Rational("80".to_i,100)
#=> "0.80"
"%.2f" % Rational("12380".to_i,100)
#=> "123.80"

Solution 5

Personally I wouldn't try to re-invent this specific wheel and go with the money gem. From the docs (emphasis added):

Features

Provides a Money class which encapsulates all information about an certain amount of money, such as its value and its currency.

Provides a Money::Currency class which encapsulates all information about a monetary unit.

Represents monetary values as integers, in cents. This avoids floating point rounding errors.

Represents currency as Money::Currency instances providing an high level of flexibility.

Provides APIs for exchanging money from one currency to another.

Has the ability to parse a money and currency strings into the corresponding Money/Currency object.

Share:
11,226
ma11hew28
Author by

ma11hew28

Updated on June 05, 2022

Comments

  • ma11hew28
    ma11hew28 almost 2 years

    I want to convert from cents to dollars correctly in Ruby. I will never have to work with fractions of cents.

    Is it possible to do this correctly (without floating point errors) without having to use BigDecimal?

    E.g., cents to dollars

    "99" => "0.99"
    "324" => "3.24"
    

    The following seems to work, but is it correct?

    (cents.to_i/100.0).to_s
    

    Update: I noticed the line above doesn't work if cents = "10287349283923497624861294712974892742837833".