2011-10-13 79 views
0

我正在以下舍入誤差當我試圖單元測試下面的類:Ruby Float Round錯誤錯誤?

class TypeTotal 
    attr_reader :cr_amount, :dr_amount, 
       :cr_count, :dr_count 

    def initialize() 
     @cr_amount=Float(0); @dr_amount=Float(0) 
     @cr_count=0;   @dr_count= 0 
    end 

    def increment(is_a_credit, amount, count=1) 
     case is_a_credit   
     when true 
      @cr_amount = Float(amount)+ Float(@cr_amount) 
      @cr_count += count 
     when false 
      @dr_amount = Float(amount)+ Float(@dr_amount) 
      @dr_count += count 
     end 
    end 
end 

單元測試:

require_relative 'total_type' 
require 'test/unit' 

class TestTotalType < Test::Unit::TestCase 
    #rounding error 
    def test_increment_count() 
    t = TypeTotal.new() 
     t.increment(false, 22.22, 2)  
     t.increment(false, 7.31, 3) 
    assert_equal(t.dr_amount, 29.53)  
    end 
end 

輸出:

1) Failure: 
test_increment_count(TestTotalType) [total_type_test.rb:10]: 
<29.529999999999998> expected but was 
<29.53>. 

1 tests, 1 assertions, 1 failures, 0 errors, 0 skips 

我使用浮動,因爲它被推薦在Pick Axe賬戶中用於估值因爲他們不應該由圓的錯誤有效。

我在Windows 7 64位家庭版和Windows XP 32位專業版上運行於ruby 1.9.2p290 (2011-07-09) [i386-mingw32]

我已經試過

  • 鑄造我變量浮動
  • 去除+ =和拼寫出增量

行爲隨機出現:

  • 12.22 + 7.31作品
  • 11.11 + 7.31怎麼回事錯不起作用
  • 11.111 + 7.31作品

任何想法?

+2

無論你做什麼,[不要將它作爲錯誤!](http://redmine.ruby-lang.org/projects/ruby/wiki/HowToReject) –

回答

2

的解決方案是使用大十進制:

require 'bigdecimal' 

class TypeTotal 
    attr_reader :cr_amount, :dr_amount, 
       :cr_count, :dr_count 

    def initialize() 
     @cr_amount=BigDecimal.new("0"); @cr_count=0, 
     @dr_amount=BigDecimal.new("0"); @dr_count=0 

    end 

    def increment(is_a_credit, amount, count=1) 
     bd_amount = BigDecimal.new(amount) 
     case is_a_credit   
     when true 
      @cr_amount= bd_amount.add(@cr_amount, 14) 
      @cr_count += count 
     when false 
      @dr_amount= bd_amount.add(@dr_amount, 14) 
      @dr_count = count 
     end 
    end 

的鋤(P53)本書中使用浮動貨幣作爲一個例子,但有一個腳註說明您當顯示數值或使用大十進制時,需要添加0.5分。

感謝您的幫助!

5

你確定這是給出的建議嗎?我期望的建議是而不是使用浮動,正是因爲他們使用二進制浮點運算,所以容易舍入誤差。來自Float documentation

浮點對象使用本機體系結構的雙精度浮點表示形式表示不精確的實數。

如果您可以引用您指的確切建議,那將有所幫助。

我建議你用BigDecimal來替代,或者用一個帶有「仙」或「幾百仙」或類似的隱含單位的整數。

+1

不能相信我錯過了腳註解釋爲什麼這個例子是錯誤的...我想它顯示你應該在良好休息的腦上編碼:) – Nathan

2

浮動問題已經提到。

當您使用浮動測試時,不應使用assert_equal,而應使用assert_in_delta

例子:

require 'test/unit' 

class TestTotalType < Test::Unit::TestCase 
    TOLERANCE = 1E-10 #or another (small) value 

    #rounding error 
    def test_increment_count() 
    t = TypeTotal.new() 
     t.increment(false, 22.22, 2)  
     t.increment(false, 7.31, 3) 
    #~ assert_equal(t.dr_amount, 29.53) #may detect float problems 
    assert_in_delta(t.dr_amount, 29.53, TOLERANCE)  
    end 
end