Ошибка с ошибками в Ruby Float? - PullRequest
       11

Ошибка с ошибками в Ruby Float?

0 голосов
/ 13 октября 2011

Я получаю следующее ошибка округления , когда я пытаюсь выполнить модульное тестирование класса ниже:

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

Я использую Число с плавающей запятой , потому что это было рекомендовано в книге «Кирка» для значений в долларах, потому что они не должны быть эффективными при ошибках округления.

Я работаю на ruby 1.9.2p290 (2011-07-09) [i386-mingw32] в 64-разрядной версии Windows 7 Home и 32-разрядной версии Windows XP Pro.

Я пробовал

  • приведение моих переменных к числам с плавающей запятой
  • удаление + = и запись увеличения

Поведение выглядит случайным:

  • 12,22 + 7,31 работ
  • 11.11 + 7.31 не работает
  • 11.111 + 7.31 работ

Есть идеи, что не так?

Ответы [ 3 ]

5 голосов
/ 13 октября 2011

Вы уверены, что это был совет? Я ожидаю, что совет будет использовать , а не , чтобы использовать Float, именно потому, что они используют двоичную арифметику с плавающей запятой, и поэтому подвержены ошибкам округления. Из документации Float :

Объекты с плавающей точкой представляют неточные действительные числа, используя представление с плавающей точкой двойной точности собственной архитектуры.

Если бы вы могли процитировать точный совет, на который вы ссылаетесь, это помогло бы.

Я бы предложил вам использовать BigDecimal вместо этого или использовать целое число с неявными единицами "центов" или "сотен центов" или чем-то подобным.

2 голосов
/ 13 октября 2011

Решением было использование большого десятичного числа:

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) в качестве примера использовалось значение float для валюты, но в нем было примечание, объясняющее, что вам нужно либо добавить 0,5 цента ккогда вы отображаете значение или используете Big Decimal.

Спасибо за вашу помощь!

2 голосов
/ 13 октября 2011

Проблемы с плавающей точкой уже упоминались.

При тестировании с плавающей точкой не следует использовать 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        
...