开发者

Convert cents into dollar string in Ruby without use of BigDecimal

I want to con开发者_如何学Pythonvert 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".


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'


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"


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.


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.


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

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


These answers are fairly old, so I wanted the next person to know there's an easier way (if you're using Rails).

ActiveSupport::NumberHelper.number_to_currency(111048.fdiv(100))

There's currency and precision options. See Rails documentation


You can use fdiv for this purpose. It returns the floating point result after division of two numbers

-> price.to_i.fdiv(100)

For example: '123'.to_i.fdiv(100) -> 1.23

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜