0

当我尝试对以下类进行单元测试时,出现以下舍入错误:

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 Ax 书中被推荐用于美元价值,因为它们不应该因回合错误而有效。

ruby 1.9.2p290 (2011-07-09) [i386-mingw32]在 Windows 7 64 位 Home 和 Windows XP 32 位 Pro 上运行。

我试过了

  • 将我的变量转换为浮点数
  • 删除 += 并拼出增量

行为出现随机:

  • 12.22 + 7.31 作品
  • 11.11 + 7.31 不起作用
  • 11.111 + 7.31 作品

任何想法出了什么问题?

4

3 回答 3

5

你确定这是给出的建议吗?我希望建议不要使用浮点数,正是因为它们使用二进制浮点运算,因此容易出现舍入错误。从浮动文档

浮点对象使用本机架构的双精度浮点表示来表示不精确的实数。

如果您可以引用您所指的确切建议,那将有所帮助。

我建议您BigDecimal改用,或使用具有“美分”或“数百美分”或类似单位的隐含单位的整数。

于 2011-10-13T11:20:58.600 回答
2

已经提到了浮动的问题。

使用浮点数进行测试时,不应使用assert_equalbut 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        
于 2011-10-13T18:40:15.177 回答
2

解决方案是使用 Big Decimal:

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

Pick Ax (p53) 书以浮点数作为货币示例,但有一个脚注说明您需要在显示值时添加 0.5 美分或使用 Big Decimal。

谢谢你的帮助!

于 2011-10-13T19:20:09.390 回答