2015-08-25 92 views
2

我正在使用Rails的钱轨宝石处理钱列。如何防止Ruby钱浮点错误

有什么办法可以防止发生浮点错误吗? (即使一个黑客会做什么,我只是想确保没有这样的错误呈现给最终用户)

Rspec的情况为例:

it "correctly manipulates simple money calculations" do 
    # Money.infinite_precision = false or true i've tried both 
    start_val = Money.new("1000", "EUR") 
    expect(start_val/30 * 30).to eq start_val 
    end 

结果

Failure/Error: expect(start_val/30 * 30).to eq start_val 

    expected: #<Money fractional:1000.0 currency:EUR> 
     got: #<Money fractional:999.99999999999999999 currency:EUR> 

    (compared using ==) 

    Diff: 
    @@ -1,2 +1,2 @@ 
    -#<Money fractional:1000.0 currency:EUR> 
    +#<Money fractional:999.99999999999999999 currency:EUR> 

回答

4

您应该使用小数为金钱金额。例如见http://ruby-doc.org/stdlib-2.1.1/libdoc/bigdecimal/rdoc/BigDecimal.html。它有任意的精确算术。

编辑:在你的情况你应该改变你的Rspec的东西,如:

it "correctly manipulates simple money calculations" do 
    # Money.infinite_precision = false or true i've tried both 
    start_val = Money.new("1000", "EUR") 
    thirty = BigDecimal.new("30") 
    expect(start_val/thirty * thirty).to eq start_val 
end 

EDIT2:在这种情况下,很1000/30不能表示为有限小数。您必须使用Rational课程或做四舍五入。示例代码:

it "correctly manipulates simple money calculations" do 
    # Money.infinite_precision = false or true i've tried both 
    start_val = Money.new("1000", "EUR") 
    expect(start_val.amount.to_r/30.to_r * 30.to_r).to eq start_val.amount.to_r 
end 
+0

如果你看看Ruby的钱宝石源,([链接](https://github.com/RubyMoney/money/blob/master/lib/money/money.rb# L251)),它已经在处理金额为BigDecimals –

+0

没有用,加上:'1000.to_d/30.to_d * 30.to_d#=>#' –

+3

1000/30不能用十进制有限制地表示。期。使用什么十进制数据类型并不重要,除非您使用无限内存的计算机,1000/30 *必须四舍五入。但它可以用三元表示,但是:1020.1。 –