Pregunta

float f = 0.7;
if( f == 0.7 )
    printf("equal");
else
    printf("not equal");

¿Por qué la salida not equal ?

¿Por qué pasó esto?

¿Fue útil?

Solución

Esto sucede porque en su declaración

  if(f == 0.7)

el 0,7 se trata como un doble.Pruebe 0.7f para asegurarse de que el valor se trate como flotante:

  if(f == 0.7f)

Pero como Michael sugirió en los comentarios a continuación, nunca debes probar la igualdad exacta de los valores de punto flotante.

Otros consejos

Esta respuesta a complementar los ya existentes: 0,7 en cuenta que no se puede representar con exactitud, ya sea como un flotador (o como un doble). Si se representa con exactitud, entonces no habría ninguna pérdida de información al convertir a flotar y luego de vuelta a doblar, y no tendría este problema.

Incluso se podría argumentar que debe haber un compilador de advertencia para las constantes de coma flotante literales que no se pueden representar con exactitud, especialmente cuando el estándar es tan difusa con respecto a si el redondeo se efectuará en tiempo de ejecución en el modo que tiene ha establecido como el tiempo o en tiempo de compilación en otro modo de redondeo.

Todos los números no enteros que pueden ser representadas exactamente tener 5 como su último dígito decimal. Por desgracia, lo contrario no es cierto: algunos números han 5 como su último dígito decimal y no se puede representar exactamente. Enteros pequeños todos se pueden representar con exactitud, y la división por una potencia de 2 transforma un número que puede representarse en otra que puede ser representado, siempre y cuando no se introduce el reino de los números no normalizados.

En primer lugar, miremos el interior del número flotante.Tomo 0.1f tiene 4 bytes de longitud (binary32), en hexadecimal es
3D CC CC CD.
Según el estándar IEEE 754 para convertirlo a decimal debemos hacer así:

enter image description here
En binario 3D CC CC CD es
0 01111011 1001100 11001100 11001101
aquí el primer dígito es un bit de signo.0 significa (-1)^0 que nuestro número es positivo.
Los segundos 8 bits son un exponente.En binario es 01111011 - en decimal 123.Pero el exponente real es 123-127 (siempre 127) =-4, significa que necesitamos multiplicar el número que obtendremos por 2^(-4).
Los últimos 23 bytes son la precisión significativa.Allí, el primer bit lo multiplicamos por 1/(2^1) (0,5), el segundo por 1/(2^2) (0,25) y así sucesivamente.Esto es lo que obtenemos:


enter image description here enter image description here

Necesitamos sumar todos los números (potencia de 2) y agregarle 1 (siempre 1, por defecto).Es
1,60000002384185791015625
Ahora multipliquemos este número por 2^(-4), es del exponente.Simplemente dividimos el número anterior entre 2 cuatro veces:
0,100000001490116119384765625
Usé la calculadora MS


**

Ahora la segunda parte.Conversión de decimal a binario.

**
tomo el numero 0.1
Es fácil porque no hay una parte entera.Primer bit de signo: es 0.La precisión del exponente y el significado la calcularé ahora.La lógica es multiplicar por 2 números enteros (0.1*2=0.2) y si es mayor que 1 restar y continuar.
enter image description here
Y el número es .00011001100110011001100110011, el estándar dice que debemos desplazarnos a la izquierda antes de obtener 1 (algo).Como ves necesitamos 4 turnos, a partir de este número calculamos Exponente(127-4=123).Y la precisión significativa ahora es
10011001100110011001100(y hay bits perdidos).
Ahora el número entero.bit de signo 0 El exponente es 123 (01111011) y la precisión significativa es 10011001100110011001100 y entero es
00111101110011001100110011001100 comparémoslo con los que tenemos del capítulo anterior.
00111101110011001100110011001101
Como puede ver, los últimos bits no son iguales.Es porque trunco ​​el número.La CPU y el compilador saben que hay algo después de que la precisión significativa no se puede mantener y simplemente establecen el último bit en 1.

El problema que estamos enfrentando es, como han dicho otros comentaristas, que por lo general es peligroso para la prueba de equivalencia exacta entre flotantes, como errores de inicialización, o errores de redondeo en los cálculos puede introducir diferencias menores que hará que el operador == para return false.

Una mejor práctica es hacer algo como

float f = 0.7;
if( fabs(f - 0.7) < FLT_EPSILON )
    printf("equal");
else
    printf("not equal");

Si se asume que FLT_EPSILON se ha definido como un valor flotante adecuadamente pequeño para su plataforma.

Dado que los errores de redondeo o de inicialización será poco probable que supere el valor de FLT_EPSILON, esto le dará el examen de equivalencia confiable que usted está buscando.

Muchas de las respuestas en la web cometen el error de mirar la diferencia abosulute entre los números de punto flotante, esto sólo es válido para los casos especiales, la forma sólida es mirar a la diferencia relativa que en continuación:

      // Floating point comparison:

        bool CheckFP32Equal(float referenceValue, float value)
        {
           const float fp32_epsilon = float(1E-7);
           float abs_diff = std::abs(referenceValue - value);

           // Both identical zero is a special case
           if( referenceValue==0.0f && value == 0.0f)
              return true;

           float rel_diff = abs_diff / std::max(std::abs(referenceValue) , std::abs(value) ); 

           if(rel_diff < fp32_epsilon)
                 return true;
           else 
                 return false;

        }

Otra pregunta exacta cerca estaba vinculado a éste por lo tanto la respuesta tardía años. No pienso que las respuestas anteriores son completas.

int fun1 ( void )
{
      float x=0.7;
      if(x==0.7) return(1);
      else       return(0);
}
int fun2 ( void )
{
      float x=1.1;
      if(x==1.1) return(1);
      else       return(0);
}
int fun3 ( void )
{
      float x=1.0;
      if(x==1.0) return(1);
      else       return(0);
}
int fun4 ( void )
{
      float x=0.0;
      if(x==0.0) return(1);
      else       return(0);
}
int fun5 ( void )
{
      float x=0.7;
      if(x==0.7f) return(1);
      else       return(0);
}
float fun10 ( void )
{
    return(0.7);
}
double fun11 ( void )
{
    return(0.7);
}
float fun12 ( void )
{
    return(1.0);
}
double fun13 ( void )
{
    return(1.0);
}

Disassembly of section .text:

00000000 <fun1>:
   0:   e3a00000    mov r0, #0
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e3a00000    mov r0, #0
   c:   e12fff1e    bx  lr

00000010 <fun3>:
  10:   e3a00001    mov r0, #1
  14:   e12fff1e    bx  lr

00000018 <fun4>:
  18:   e3a00001    mov r0, #1
  1c:   e12fff1e    bx  lr

00000020 <fun5>:
  20:   e3a00001    mov r0, #1
  24:   e12fff1e    bx  lr

00000028 <fun10>:
  28:   e59f0000    ldr r0, [pc]    ; 30 <fun10+0x8>
  2c:   e12fff1e    bx  lr
  30:   3f333333    svccc   0x00333333

00000034 <fun11>:
  34:   e28f1004    add r1, pc, #4
  38:   e8910003    ldm r1, {r0, r1}
  3c:   e12fff1e    bx  lr
  40:   66666666    strbtvs r6, [r6], -r6, ror #12
  44:   3fe66666    svccc   0x00e66666

00000048 <fun12>:
  48:   e3a005fe    mov r0, #1065353216 ; 0x3f800000
  4c:   e12fff1e    bx  lr

00000050 <fun13>:
  50:   e3a00000    mov r0, #0
  54:   e59f1000    ldr r1, [pc]    ; 5c <fun13+0xc>
  58:   e12fff1e    bx  lr
  5c:   3ff00000    svccc   0x00f00000  ; IMB

¿Por qué fun3 y fun4 volver una y no los otros? ¿Por qué funciona fun5?

Se trata de la lengua. La lengua dice que el 0,7 es un doble a menos que utilice esta sintaxis 0.7f entonces es una sola. Así

  float x=0.7;

el doble 0,7 se convierte en un único y se almacena en x.

  if(x==0.7) return(1);

La lengua dice que tenemos que promover a la mayor precisión por lo que la única en X se convierte en un doble y doble en comparación con el 0,7.

00000028 <fun10>:
  28:   e59f0000    ldr r0, [pc]    ; 30 <fun10+0x8>
  2c:   e12fff1e    bx  lr
  30:   3f333333    svccc   0x00333333

00000034 <fun11>:
  34:   e28f1004    add r1, pc, #4
  38:   e8910003    ldm r1, {r0, r1}
  3c:   e12fff1e    bx  lr
  40:   66666666    strbtvs r6, [r6], -r6, ror #12
  44:   3fe66666    svccc   0x00e66666

sola 3f333333 doble 3fe6666666666666

Como se señaló Alexandr si esa respuesta sigue siendo IEEE 754 es un único

  

seeeeeeeefffffffffffffffffffffff

Y es doble

  

seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff

con 52 bits de fracción en lugar de los 23 que individual tiene.

00111111001100110011... single
001111111110011001100110... double

0 01111110 01100110011... single
0 01111111110 01100110011... double

Al igual que 1 / 3º en la base 10 es 0,3333333 ... para siempre. Tenemos un patrón que se repite aquí 0110

01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.

Y aquí está la respuesta.

  if(x==0.7) return(1);

X contiene 01100110011001100110011 como su fracción, cuando eso se convierte de nuevo al doble de la fracción es

01100110011001100110011000000000....

que no es igual a

01100110011001100110011001100110...

pero aquí

  if(x==0.7f) return(1);

eso no ocurra la promoción de los mismos patrones de bits se comparan entre sí.

¿Por qué el trabajo 1.0?

00000048 <fun12>:
  48:   e3a005fe    mov r0, #1065353216 ; 0x3f800000
  4c:   e12fff1e    bx  lr

00000050 <fun13>:
  50:   e3a00000    mov r0, #0
  54:   e59f1000    ldr r1, [pc]    ; 5c <fun13+0xc>
  58:   e12fff1e    bx  lr
  5c:   3ff00000    svccc   0x00f00000  ; IMB

0011111110000000...
0011111111110000000...

0 01111111 0000000...
0 01111111111 0000000...

En ambos casos la fracción es todo ceros. Así que la conversión de doble a sencillo a doble no hay una pérdida de precisión. Se convierte de simple a doble exactitud y la comparación bit de los dos valores funciona.

El más votos y comprueba respuesta por Halfdan es la respuesta correcta, se trata de un caso de precisión mixta y que nunca debe hacer una comparación es igual.

El no era por eso se muestra en esa respuesta. 0.7 1.0 funciona falla. ¿Por qué fracasan 0,7 suponía muestra. Una pregunta duplicado 1.1 falla también.


editar

Los iguales se puede sacar del problema aquí, es una cuestión diferente que ya ha sido contestada, pero es el mismo problema y también tiene el "qué ..." shock inicial.

int fun1 ( void )
{
      float x=0.7;
      if(x<0.7) return(1);
      else       return(0);
}
int fun2 ( void )
{
      float x=0.6;
      if(x<0.6) return(1);
      else       return(0);
}

Disassembly of section .text:

00000000 <fun1>:
   0:   e3a00001    mov r0, #1
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e3a00000    mov r0, #0
   c:   e12fff1e    bx  lr

¿Por qué se muestran como menos y el otro no menos de? Cuando deberían ser iguales.

Desde arriba sabemos la historia 0.7.

01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.

01100110011001100110011000000000....

es menor que.

01100110011001100110011001100110...

0.6 es un patrón de repetición diferente 0011 en lugar de 0110.

pero cuando se convierten de un doble con una sola o en general cuando se representa como una sola IEEE 754.

00110011001100110011001100110011.... double 52 bits.
00110011001100110011001 is NOT the fraction for single
00110011001100110011010 IS the fraction for single

IEEE 754 utiliza el redondeo modos, redondear, redondee hacia abajo o redondo a cero. Los compiladores tienden a redondear por defecto. Si recuerda el redondeo en la escuela primaria 12345678 si quería redondear al 3 dígitos de la parte superior sería 12300000, pero ronda a la siguiente cifra 1.235.000 si el dígito después es 5 o mayor que redondear hacia arriba. 5 es un medio de 10 la base (decimal) en binario 1 es un medio de la base de modo que si el dígito después de la posición que queremos ronda es 1 luego se redondea hasta otro no haga. Así que para que nos dio 0,7 redondeo al alza de 0,6 por nosotros redondeamos.

Y ahora es fácil ver que

00110011001100110011010

convertido a un doble debido a (x <0,7)

00110011001100110011010000000000....

es mayor que

00110011001100110011001100110011....

Así que sin tener que hablar sobre el uso es igual a la cuestión todavía se presenta es el doble de 0,7 0.7f es única, la operación es ascendido a la máxima precisión si difieren.

Considere esto:

int main()
{
    float a = 0.7;
    if(0.7 > a)
        printf("Hi\n");
    else
        printf("Hello\n");
    return 0;
}

si (0.7> a) aquí a es una variable flotador y 0.7 es una constante doble. El 0.7 doble constante es mayor que la variable float a. De ahí que la si la condición se cumple y se imprime 'Hi'

Ejemplo:

int main()
{
    float a=0.7;
    printf("%.10f %.10f\n",0.7, a);
    return 0;
}

Salida:
0,7000000000 0,6999999881

Si cambia el tipo de datos de f doble , que va a imprimir igual Esto se debe a las constantes de punto flotante almacenan en doble y no flotando en largo , de doble precisión es alta y el flotador ha menos preciso doble valor, almacenada en 64 binario y el flotador almacenada en 32 bit binario, que va a ser completamente claro si ves el método de conversión de números de punto flotante a la conversión binaria.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top