2010-04-07 4 views
5

У меня есть расчет, который генерирует то, что, как представляется, Float 22,23 и буквальным 22,23 так:Зачем Ruby терпеть неудачу на 2 поплавках, которые выглядят одинаково?

some_object.total => 22.23 
some_object.total.class => Float 

22.23 => 22.23 
22.23.class => Float 

Но по какой-то причине, следующее неверно:

some_object.total == 22.23 ? true : false 

Необычное, право ?

Используется ли какой-то механизм точности, который, возможно, не полностью прозрачен через вызов some_object.total?

+0

Какая версия Ruby? 1.8.6 имеет некоторые арифметические ошибки с плавающей точкой. – mckeed

+0

Привет, вы также можете проверить мое сообщение по вопросам с плавающей точкой http://vladzloteanu.wordpress.com/2010/01/11/why-you-shouldnt-use-float-for-currency-floating-point-issues-explained -for-ruby-and-ror/ –

ответ

9

Номера с плавающей запятой не могут точно представлять все десятичные числа в пределах их диапазона. Например, 0,9 не точно 0,9, это число действительно близко к 0,9, которое заканчивается печатанием, как это в большинстве случаев. Поскольку вы выполняете вычисления с плавающей запятой, эти ошибки могут накапливаться, и вы заканчиваете что-то очень близкое к правильному числу, но не точно равное ему. Например, 0.3 * 3 == 0.9 вернет false. Это относится к каждому компьютерному языку, который вы когда-либо использовали, - это просто математическая математика с плавающей запятой. См., Например, this question about Haskell.

Чтобы проверить равенство с плавающей точкой, вы обычно хотите проверить, находится ли этот номер в пределах небольшого диапазона цели. Так, например:

def float_equal(a, b) 
    if a + 0.00001 > b and a - 0.00001 < b 
    true 
    else 
    false 
    end 
end 

Вы можете также использовать класс BigDecimal в Ruby, для представления произвольных десятичных чисел.

Если это тест случае, вы можете использовать assert_in_delta:

def test_some_object_total_is_calculated_correctly 
    assert_in_delta 22.23, some_object.total, 0.01 
end 
+0

Да, это помогает! Благодаря! – btelles

+2

Как раз для того, чтобы сделать очевидным, если это не из сообщения Чака, это не ограничение рубиновых чисел с плавающей запятой. Это ограничение с плавающей запятой, период. Он работает таким образом на всех языках, где используется плавающая точка IEEE. Численные вопросы - важная и несколько глубокая тема, которую вы могли бы затронуть в какой-то момент в любой учебной программе по информатике. – frankc

+0

Спасибо, брат. Метод assert_in_delta сделал это для меня. Я получал: «<3375.0> ожидал, но был <3375.0>» и не понял, что происходит! – mjnissim

1

Здесь что-то еще происходит. это от 1.8.7 IRB

irb(main):001:0> class Test 
irb(main):002:1> attr_accessor :thing 
irb(main):003:1> end 
=> nil 
irb(main):004:0> t = Test.new 
=> #<Test:0x480ab78> 
irb(main):005:0> t.thing = 22.5 
=> 22.5 
irb(main):006:0> t.thing == 22.5 
=> true 
+0

сумма фактически представляет собой сумму и расчет нескольких других чисел, вызывая неточности. – btelles

6

Float#to_s и Float#inspect раунде. Попробуйте "%.30f" % some_object.total, и вы увидите, что это не совсем 22.23.

+0

heh ... "22.229999999999996873611962655559" Все. Благодарю. – btelles

Смежные вопросы