问题
I'm testing a small and simple library I made in Ruby. The goal is to convert from EUR
to CNY
and vice versa. Simple.
I tested it to be sure everything works but I got an unexpected issue. When I use to_euro
followed by to_yuan
it should go back to the original amount
; it doesn't happen. I tried to .to_f
or round(2)
the amount
variable which fix some tests, raise new ones, but it's never equal to what I expect globally ; I'm running out of idea to fix this :(
class Currency
attr_reader :amount, :currency
def initialize(amount, currency='EUR')
@amount = amount
@currency = currency
end
def to_yuan
update_currency!('CNY', amount * Settings.instance.exchange_rate_to_yuan)
end
def to_euro
update_currency!('EUR', amount / Settings.instance.exchange_rate_to_yuan)
end
def display
"%.2f #{current_symbol}" % amount
end
private
def current_symbol
if currency == 'EUR'
symbol = Settings.instance.supplier_currency.symbol
elsif currency == 'CNY'
symbol = Settings.instance.platform_currency.symbol
end
end
def update_currency!(new_currency, new_amount)
unless new_currency == currency
@currency = new_currency
@amount = new_amount
end
self
end
end
Tests
describe Currency do
let(:rate) { Settings.instance.exchange_rate_to_yuan.to_f }
context "#to_yuan" do
it "should return Currency object" do
expect(Currency.new(20).to_yuan).to be_a(Currency)
end
it "should convert to yuan" do
expect(Currency.new(20).to_yuan.amount).to eql(20.00 * rate)
end
it "should convert to euro and back to yuan" do
# state data test
currency = Currency.new(150, 'CNY')
expect(currency.to_euro).to be_a(Currency)
expect(currency.to_yuan).to be_a(Currency)
expect(currency.amount).to eql(150.00)
end
end
context "#to_euro" do
it "should convert to euro" do
expect(Currency.new(150, 'CNY').to_euro.amount).to eql(150 / rate)
end
end
context "#display" do
it "should display euros" do
expect(Currency.new(10, 'EUR').display).to eql("10.00 €")
end
it "should display yuan" do
expect(Currency.new(60.50, 'CNY').display).to eql("60.50 ¥")
end
end
end
And here's my RSpec result
I'm pretty sure this problem is very common, any idea how to solve it easily ?
回答1:
Float isn't an exact number representation, as stated in the ruby docs:
Float objects represent inexact real numbers using the native architecture's double-precision floating point representation.
This not ruby fault, as floats can only be represented by a fixed number of bytes and therefor cannot store decimal numbers correctly.
Alternatively, you can use ruby Rational or BigDecimal
Its is also fairly common to use the money gem when dealing with currency and money conversion.
来源:https://stackoverflow.com/questions/38702439/float-not-equal-after-division-followed-by-multiplication