当我尝试对下面的课进行单元测试时,我收到以下舍入错误:
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
我正在使用浮动,因为在拣选斧书中建议使用美元值,因为它们不应该因圆形错误而生效。
我在Windows 7 64位Home和Windows XP 32位Pro上运行ruby 1.9.2p290 (2011-07-09) [i386-mingw32]
。
我试过
行为似乎是随机的:
任何想法都出错了?
答案 0 :(得分:5)
你确定这是给出的建议吗?我希望不的建议使用Floats,正是因为它们使用二进制浮点算法,所以 容易出现舍入错误。来自Float documentation:
Float对象使用本机架构的双精度浮点表示来表示不精确的实数。
如果你能引用你所指的确切建议,那会有所帮助。
我建议您改用BigDecimal
,或者使用隐含单位为“美分”或“数百美分”或类似内容的整数。
答案 1 :(得分: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
答案 2 :(得分: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 Axe(p53)书使用浮动货币作为一个例子,但有一个脚注说明你需要在显示值时增加.5美分或使用大十进制。
谢谢你的帮助!