2010-04-07 8 views
5

tengo un cálculo que genera lo que parece ser el flotador 22.23, y un literal 22.23 de este modo:¿Por qué Ruby fallaría la igualdad en 2 carrozas que parecen iguales?

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

22.23 => 22.23 
22.23.class => Float 

Pero por alguna razón, la siguiente es falsa:

some_object.total == 22.23 ? true : false 

raro, a la derecha ?

¿Hay algún tipo de mecanismo de precisión que quizás no sea completamente transparente a través de la llamada some_object.total?

+0

¿Qué versión de Ruby? 1.8.6 tiene algunos errores aritméticos flotantes. – mckeed

+0

Hola, también puedes consultar mi publicación sobre problemas de coma flotante http://vladzloteanu.wordpress.com/2010/01/11/why-you-shouldnt-use-float-for-currency-floating-point-issues-explained -for-ruby-and-ror/ –

Respuesta

9

Los números de coma flotante no pueden representar con precisión todos los números decimales dentro de su rango. Por ejemplo, 0.9 no es exactamente 0.9, es un número realmente cercano a 0.9 que termina imprimiéndose como en la mayoría de los casos. Al hacer cálculos de coma flotante, estos errores pueden acumularse y terminar con algo muy cercano al número correcto pero no exactamente igual a él. Por ejemplo, 0.3 * 3 == 0.9 devolverá false. Este es el caso en todos los lenguajes de cómputo que usará en su vida, así es como funciona la matemática binaria flotante. Ver, por ejemplo, this question about Haskell.

Para probar la igualdad de coma flotante, generalmente quiere comprobar si el número se encuentra dentro de un rango muy pequeño del objetivo. Así, por ejemplo:

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

también puede utilizar la clase BigDecimal en Ruby para representar números decimales arbitrarias.

Si se trata de un caso de prueba, puede utilizar assert_in_delta:

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

¡Sí, eso ayuda! ¡Gracias! – btelles

+2

Solo para ser obvio si no es de la publicación de Chuck, esta no es una limitación de punto flotante ruby. Esta es una limitación de punto flotante, punto. Funciona de esta manera en todos los lenguajes que usan el punto flotante IEEE. Los temas numéricos son un tema importante y algo profundo que cubrirías en algún momento en cualquier plan de estudios de ciencias de la computación. – frankc

+0

Gracias hermano. El método assert_in_delta lo hizo por mí. Me estaba dando: "<3375.0> esperado, pero era <3375.0>" y no entendía lo que estaba pasando! – mjnissim

1

Hay algo más pasando aquí. esto es de una 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

el total es en realidad una suma y el cálculo de varios otros números, causando las imprecisiones. – btelles

6

Float#to_s y Float#inspect ronda. Pruebe "%.30f" % some_object.total y verá que no es exactamente 22.23.

+0

heh ... "22.229999999999996873611962655559" Eso es todo. Gracias. – btelles

Cuestiones relacionadas