En algún momento en un algoritmo necesito para comparar el float valor de una propiedad de una clase, a un flotador. Así que hacer esto:
if (self.scroller.currentValue <= 0.1) {
}
donde currentValue es un float de la propiedad.
Sin embargo, cuando tengo la igualdad y la self.scroller.currentValue = 0.1
la declaración si no se cumple, y el código no se ejecuta! Me enteré de que me puede solucionar este problema mediante la fundición de 0,1 a flotar. Como este:
if (self.scroller.currentValue <= (float)0.1) {
}
Esto funciona bien.
Puede alguien explicar a mi por qué está sucediendo esto? Es de 0,1 define como un doble por defecto o algo?
Gracias.
- Ver también, «Lo que Cada Equipo Científico Debe Saber Sobre la Aritmética de Punto Flotante:» docs.sun.com/source/806-3568/ncg_goldberg.html
- Para aquellos de ustedes (especialmente @alastair) que han trabajado para mejorar mi respuesta, no estoy convencido de que podría ser mejorado. Estoy de acuerdo en que era incorrecto y probablemente peligroso. He eliminado. Por favor, ver a James Snook de respuesta para una exploración más profunda de este no-problema trivial.
- Tengo que decir que me pareció que vale la pena mantener la respuesta, con las correcciones y añadidos, pero entiendo tu perspectiva. Disculpas si mi edición parecía un poco agresivo, sólo quería dejar claro cuál era el problema.
- No, en absoluto; ayudó a deshacerse de algo creo que engañaba a la gente (incluido yo mismo). Prefiero mejorar James Snook de respuesta con cualquier cosa que quisiera agregar. La incorrección en por LO tanto, incluso marcó mal, a veces puede ser confuso para los lectores. Si su rep no te deja ver los mensajes eliminados, y que desea copiar algo que escribí anteriormente en su propia respuesta o James, he publicado el antiguo texto aquí (y también para cualquier persona curiosa de lo que estamos hablando :D): gist.github.com/rnapier/78502480e53f526d24f30a14032dea8d)
Creo que, no habiendo encontrado la norma que dice que es así, que cuando se compara un
float
a undouble
lafloat
es arrojado a undouble
antes de comparar. Números de punto flotante sin un modificador se considerandouble
en C.Sin embargo, en C no hay ninguna representación exacta de 0.1 en carrozas y dobles. Ahora, usando un flotador le da un pequeño error. El uso de una doble le da una aún más pequeña de error. El problema ahora es, que mediante la conversión de los
float
a undouble
que llevar sobre el más grande de error de lafloat
. Por supuesto que no se ha ido comparar la igualdad de ahora.Lugar de utilizar
(float)0.1
usted podría utilizar0.1f
que es un poco más agradable para leer.El problema es, como han sugerido en su pregunta, de que usted está comparando un flotador con un doble.
Hay un problema más general con la comparación de carrozas, esto ocurre porque al hacer un cálculo de un número de punto flotante, el resultado de los cálculos pueden no ser exactamente lo que usted espera. Es bastante común que el último bit de la resultante de flotación se mal (a pesar de la inexactitud puede ser más grande que el último bit). Si utiliza
==
para comparar dos carrozas, a continuación, todos los bits tienen que ser el mismo para la flota a ser igual. Si el cálculo da un poco inexacta resultado entonces ellos no van a comparar la igualdad cuando se lo espere. En lugar de comparar los valores de esta, usted puede comparar, para ver si son casi iguales. Para hacer esto usted puede tomar la diferencia positiva entre la flota y ver si es menor que un valor dado (llamado epsilon).Para elegir un buen epsilon usted necesita para entender un poco acerca de los números de punto flotante. Números de punto flotante funcionan de forma similar a la representación de un número a un determinado número de cifras significativas. Si trabajamos a 5 cifras significativas y el cálculo de los resultados en el último dígito del resultado estar equivocado, a continuación, 1.2345 va a tener un error de +-0.0001 mientras que 1234500 va a tener un error de +-100. Si siempre la base de su margen de error sobre el valor 1.2345 luego comparar su rutina será idéntica a
==
para todos los valores grandes de 10 (cuando se utiliza un decimal). Esto es peor en binario, es todos los valores mayores que 2. Esto significa que la epsilon nos tiene que ser en relación con el tamaño de la flota que estamos comparando.FLT_EPSILON es la brecha entre el 1 y el más cercano de flotación. Esto significa que puede ser una buena epsilon para elegir si su número está entre el 1 y el 2, pero si su valor es mayor que 2, el uso de este epsilon es inútil porque la brecha entre el 2 y el más cercano de flotación es mayor que el de epsilon. Así que tenemos que elegir a un epsilon en relación con el tamaño de nuestra flota (como el error en el cálculo es en relación con el tamaño de nuestra flota).
Una buena(ish) de punto flotante comparar rutina se ve algo como esto:
Esta comparación rutina compara flota en relación con el tamaño de la más grande de flotación pasado.
scalbnf(1.0f, ilogb(a)) * FLT_EPSILON
encuentra la brecha entrea
y el más cercano de flotación. Este se multiplica por elepsilonMultiplier
, por lo que el tamaño de la diferencia puede ser ajustada, dependiendo de cómo es inexacto el resultado del cálculo es probable que sea.Puede hacer una simple
compareLessThan
rutina como esta:Usted podría también escribir un muy similares
compareGreaterThan
función.Vale la pena señalar que la comparación de flota como esto no siempre puede ser lo que usted desea. Por ejemplo, esto nunca va a encontrar que un flotador está cerca de 0 a menos que sea 0. Para arreglar esto se tendría que decidir qué valor se pensaba que era cercano a cero, y escribir una prueba adicional para esto.
A veces las inexactitudes que usted recibe no dependen del tamaño del resultado de un cálculo, pero dependerá de los valores que se ponen en un cálculo. Por ejemplo
sin(1.0f + (float)(200 * M_PI))
dará una mucho menos preciso resultado desin(1.0f)
(los resultados deben ser idénticos). En este caso comparar su rutina habría que mirar el número que usted pone en el cálculo para conocer el margen de error de la respuesta.epsilonMultiplier
?1
permite el más cercano de los dos flotadores para el resultado deseado para contar como iguales. El aumento se aumenta el número de valores que se va a comparar la igualdad. Usted puede razonar sobre el tamañoepsilonMultiplier
debe basarse en cómo se calcula un valor, o por la experimentación.Dobles y flotadores tienen diferentes valores para la mantisa de la tienda en binario (float es de 23 bits, doble 54). Estos casi nunca será igual.
El IEEE de Punto Flotante artículo en la wikipedia puede ayudar a entender esta distinción.
En C, un punto flotante literal como 0.1 es un doble, no un float. Dado que los tipos de los elementos de datos que se comparan son diferentes, la comparación se realiza en el tipo más precisa (doble). En todas las implementaciones que yo sé acerca de flotación tiene una menor representación del doble (generalmente se expresa como algo así como 6 vs 14 decimales). Por otra parte, la aritmética es en binario, y 1/10 no tiene una exacta representación en binario.
Por lo tanto, usted está tomando un flotador 0.1, que pierde exactitud, de la ampliación a doble, y se espera que para comparar igual a un doble 0.1, que pierde menos precisión.
Supongamos que estamos haciendo esto en decimal, con flotador de tres dígitos y haga doble seis, y estábamos comparando a 1/3.
Hemos almacenado a un valor de tipo float se 0.333. Estamos comparando una doble con valor 0.333333. Podemos convertir el flotador 0.333 a doble 0.333000, y encontrar diferentes.
0.1 realidad es muy dificil de valor a almacenar binario. En la base 2, 1/10 es infinitamente fracción de repetición
Como varios se ha señalado, la comparación ha de hacerse con una constante de exactamente la misma precisión.
En general, en cualquier idioma, realmente no se puede contar con igualdad de flotación como tipos. En su caso ya que parece que tienes más control, parece que el 0.1 no es flotar por defecto. Usted probablemente podría encontrar que con sizeof(0.1) (vs sizeof(auto.scroller.currentValue).
(a - m < b) && (a + m > b)
).Convertir una cadena, a continuación, compare: