问题描述
我正在测试一个我用 Ruby 制作的小而简单的库.目标是将 EUR
转换为 CNY
,反之亦然.简单.
我对其进行了测试以确保一切正常,但出现了意外问题.当我使用 to_euro
后跟 to_yuan
时,它应该回到原来的 amount
;它不会发生.我尝试 .to_f
或 round(2)
修复一些测试的 amount
变量,提出新的测试,但它永远不等于我全球范围内期待;我已经没有办法解决这个问题了:(
class 货币attr_reader :金额,:货币def 初始化(金额,货币 =欧元")@金额=金额@currency = 货币结尾def to_yuanupdate_currency!('CNY', amount * Settings.instance.exchange_rate_to_yuan)结尾def to_euroupdate_currency!('EUR', 金额/Settings.instance.exchange_rate_to_yuan)结尾定义显示"%.2f #{current_symbol}" % 数量结尾私人的def current_symbol如果货币 == '欧元'符号 = Settings.instance.supplier_currency.symbolelsif 货币 == 'CNY'符号 = Settings.instance.platform_currency.symbol结尾结尾def update_currency!(new_currency, new_amount)除非新货币 == 货币@currency = new_currency@amount = new_amount结尾自己结尾结尾
测试
describe Currency dolet(:rate) { Settings.instance.exchange_rate_to_yuan.to_f }上下文#to_yuan"做它应该返回货币对象"做期望(货币.新(20).to_yuan).to be_a(货币)结尾它应该转换成人民币"吗期望(Currency.new(20).to_yuan.amount).to eql(20.00 * rate)结尾它应该转换成欧元再转换成人民币"做# 状态数据测试货币 = Currency.new(150, 'CNY')期望(货币.to_euro).to be_a(货币)期望(货币.to_yuan).to be_a(货币)期望(货币.金额).到 eql(150.00)结尾结尾上下文#to_euro"做它应该转换为欧元"做期望(Currency.new(150,'CNY').to_euro.amount).to eql(150/rate)结尾结尾上下文#display"做它应该显示欧元"做期望(Currency.new(10,'EUR').显示).to eql(10.00€")结尾它应该显示元"做expect(Currency.new(60.50, 'CNY').display).to eql("60.50 ¥")结尾结尾结尾
这是我的 RSpec 结果
我很确定这个问题很常见,知道如何轻松解决吗?
Float 不是精确的数字表示,如 ruby 文档:
Float 对象使用原生架构的双精度浮点表示法来表示不精确的实数.
这不是 ruby 错误,因为浮点数只能由固定数量的字节表示,因此无法正确存储十进制数.
或者,您可以使用 ruby Rational 或 BigDecimal
在处理货币和货币兑换时,使用 money gem 也很常见.>
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 ?
Float isn't an exact number representation, as stated in the ruby docs:
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.
这篇关于除法和乘法后浮点数不相等的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!