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)
InformationsquelleAutor Dimitris | 2009-10-23

7 Comentarios

  1. 30

    Creo que, no habiendo encontrado la norma que dice que es así, que cuando se compara un float a un double la float es arrojado a un double antes de comparar. Números de punto flotante sin un modificador se consideran double 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 un double que llevar sobre el más grande de error de la float. Por supuesto que no se ha ido comparar la igualdad de ahora.

    Lugar de utilizar (float)0.1 usted podría utilizar 0.1f que es un poco más agradable para leer.

  2. 6

    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:

    bool compareNearlyEqual (float a, float b, unsigned epsilonMultiplier)       
    {
      float epsilon;
      /* May as well do the easy check first. */
      if (a == b)
        return true;
    
      if (a > b) {
        epsilon = scalbnf(1.0f, ilogb(a)) * FLT_EPSILON * epsilonMultiplier;
      } else {
        epsilon = scalbnf(1.0, ilogb(b)) * FLT_EPSILON * epsilonMultiplier;
      }
    
      return fabs (a - b) <= epsilon;
    }

    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 entre a y el más cercano de flotación. Este se multiplica por el epsilonMultiplier, 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:

    bool compareLessThan (float a, float b, unsigned epsilonMultiplier)
    {
      if (compareNearlyEqual (a, b, epsilonMultiplier)
        return false;
    
      return a < b;
    }

    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 de sin(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.

    • ¿Qué es epsilonMultiplier?
    • le permite ajustar el tamaño de la epsilon. Por ejemplo, el ajuste a 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ño epsilonMultiplier debe basarse en cómo se calcula un valor, o por la experimentación.
  3. 4

    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.

    • Siguiendo este pensamiento en decimal (que tiene diferentes números de binario, pero el concepto es el mismo), se encuentra que (1/3)*3 != 1, no importa cómo muchos (finito) dígitos que usted elija. Es por eso que hacer todos tus matemáticas en float o double en realidad no soluciona el problema.
    • A la derecha. Por supuesto, puede ser arbitrariamente cercano a 1 por el uso de los dígitos, por lo que las pruebas para acercarse funciona mucho mejor que las pruebas para la igualdad. El verdadero problema aquí es que de punto flotante de la igualdad de las pruebas no en el trabajo en general.
    • De acuerdo. Por lo tanto la disposición de advertencia (que recomiendo encender) para evitar que accidentalmente el uso de punto flotante de la igualdad.
  4. 4

    0.1 realidad es muy dificil de valor a almacenar binario. En la base 2, 1/10 es infinitamente fracción de repetición

    0.0001100110011001100110011001100110011001100110011...

    Como varios se ha señalado, la comparación ha de hacerse con una constante de exactamente la misma precisión.

  5. 1

    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).

    • que buena idea! No creo que de sizeof()
    • sizeof revela que 0,1 es un doble. Todavía es muy extraño que usted no consigue la igualdad cuando ambos tienen el valor 0.10000 ¿no?
    • no es que extraño. Ver a @MarkPowell la respuesta.
    • No, eso no es muy extraño. 0.1 no es precisamente representable en binario. Como Lou dijo, no cuentan en la igualdad de números de punto flotante. Nunca. Comprobar si los números están dentro de un pequeño margen de cada una de las otras (por ejemplo, (a - m < b) && (a + m > b)).
  6. -1

    Convertir una cadena, a continuación, compare:

    NSString* numberA = [NSString stringWithFormat:@"%.6f", a];
    NSString* numberB = [NSString stringWithFormat:@"%.6f", b];
    
    return [numberA isEqualToString: numberB];

Dejar respuesta

Please enter your comment!
Please enter your name here