2011-10-13 76 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