2012-09-27 52 views
18

Mientras que "todos sabemos" que x == y puede ser problemático, donde x y y valores de punto flotante, esta pregunta es un poco más específico:¿Está garantizado que "(flotante) entero == entero" será igual en C#?

int x = random.Next(SOME_UPPER_LIMIT); 
float r = x; 
// Is the following ALWAYS true?  
r == x 

Ahora, puesto que el rango de flotación de mucho mayor que la de los enteros (pero la precisión es insuficiente para presentar números enteros únicos en los bordes), sería bueno si las respuestas a esta pregunta también se referían a los valores de x que se pueden garantizar anteriormente, si se puede garantizar a todas.


Actualmente mi código es hacer esta suposición (para valores relativamente pequeños de x) - Me gustaría asegurarse de que no voy a conseguir mordido :)


Este fallará con "no es igual: 16777217" (float fundido -> int):

for (int i = 0; i < int.MaxValue; i++) { 
    float f = i; 
    if ((int)f != i) throw new Exception("not equal " + i); 
} 

Este código similar que no se producirá un error (sólo int -> float); Sin embargo, debido a la pérdida en la conversión, hay varios flotadores que pueden "iguales" el mismo entero, y puede representar un error en silencio:

for (int i = 0; i < int.MaxValue; i++) { 
    float f = i; 
    if (f != i) throw new Exception("not equal " + i); 
} 
+4

Pasa a través de 'Int32.MinValue' a' Int32.MaxValue', comparando los resultados del lanzamiento cada vez. Recoge los casos donde la comparación es falsa y tienes una respuesta (al menos para tu arquitectura). – Oded

+2

@pst: no creo que haya * ninguna * respuesta correcta genérica en esta pregunta, sinceramente. La suposición "siempre" nunca funcionaría en máquinas diferentes, por lo que nunca será * siempre *. Si, naturalmente, estamos hablando de una respuesta positiva aquí. – Tigran

Respuesta

13

Sí, la comparación siempre será cierta, independientemente del valor que tenga el int.

El int se convertirá en un float para hacer la conversión, y la primera conversión a float siempre dará el mismo resultado que la segunda conversión.

considerar:

int x = [any integer value]; 
float y = x; 
float z = x; 

Los valores de y y z siempre serán los mismos. Si la conversión pierde precisión, ambas conversiones perderán la precisión exactamente de la misma manera.

Si convierte el float de nuevo a int para la comparación, esa es otra cuestión.


Además, tenga en cuenta que incluso si un valor específico int convertido a float siempre da como resultado el mismo valor float, eso no quiere decir que el valor float tiene que ser único para ese valor int. Hay int valores donde (float)x == (float)(x+1) sería true.

+0

Gracias. Ese es precisamente el paso/lógica que me faltaba en la cabeza: 'y = x',' z = x', por lo tanto 'y == z'. Tiene mucho más sentido ahora. –

+1

Solo tenga en cuenta que esto es específico del idioma. Algunos idiomas pueden no requerir que el resultado de convertir un número entero en un flotante sea consistente. (Por ejemplo, uno puede realizarse en un registro con precisión adicional y uno puede realizarse en la memoria. Cuando el resultado de la memoria se obtiene y aumenta para registrar la longitud, la precisión perdida puede ocasionar que se compare mal). –

+0

+1. El formato flotante detallado se puede consultar en http://en.wikipedia.org/wiki/IEEE_754-2008. Según entiendo, la conversión 'int'->' float' es más o menos ('value & 0xFFFFFD00') por lo que las entradas se agruparán en grupos de ~ 512 valores que son iguales al mismo valor flotante. –

5

El siguiente experimento revela que la respuesta es que no tienen ese caso borde donde la igualdad no es cierto

static void Main(string[] args) 
    { 
     Parallel.For(int.MinValue, int.MaxValue, (x) => 
     { 
      float r = x; 
      // Is the following ALWAYS true?  
      bool equal = r == x; 
      if (!equal) Console.WriteLine("Unequal: " + x);     
     }); 

     Console.WriteLine("Done"); 
     Console.ReadKey(); 

     return; 
} 

parece razonable que las conversiones

float f = i; 

y

if ((int)f != i) 

deben seguir las mismas reglas. Esto prueba que las conversiones int -> float y float -> int son una biyección.

NOTA: el código del experimento en realidad no prueba el caso borde int.MaxValue debido a parámetro de Parallel.For es exclusiva, pero probó que el valor por separado y también pasa la prueba.

+0

¿Qué problema exactamente? –

+0

Ahh, ya veo, lo eché a perder en la cabeza: la dirección. –

3

me encontré con este código sin una excepción que es lanzado:

for (int x = Int16.MinValue; x < Int16.MaxValue; x++) 
{ 
float r = x; 
if (r != x) 
{ 
    throw new Exception("Failed at: " + x); 
} 
} 

A la espera de (no completar esta prueba, ya que tomó demasiado tiempo, nunca se inició una excepción, aunque mientras marcha):

for (long x = Int64.MinValue; x < Int64.MaxValue; x++) 
{ 
float r = x; 
if (r != x) 
{ 
    throw new Exception("Failed at: " + x); 
} 
} 

fue de ida y corrió su ejemplo con una advertencia, este fue el resultado:

[Exception: not equal 16777217 ?= 1.677722E+07 ?= 16777216] 

for (int i = 0; i < int.MaxValue; i++) 
{ 
float f = i; 
if ((int)f != i) throw new Exception("not equal " + i + " ?= " + f + " ?= " + (int)f); 
} 
+0

Heh. Buena suerte esperando :) La respuesta de Guffa debería poner tu corazón en reposo. –

+0

@pst - Sí, dejé de esperar jajaja, eso podría llevar ** mucho ** mucho tiempo para que esta máquina funcione. Vea mi edición para una salida extendida de su conversión de nuevo a int desde float. –

0

Mi T La comprensión de los cálculos aritméticos de punto flotante es que son manejados por la CPU, que únicamente determina su precisión. Por lo tanto, no existe un valor definido por encima del cual los flotadores pierden precisión.

Pensaba que la arquitectura x86, por ejemplo, garantizaba un mínimo, pero me han demostrado que estaba equivocado.

+0

Me interesaría saber por qué me equivoco, si me equivoco.¡Vota y escapa! –

+2

El comportamiento de C# se especifica mediante un documento de estándares. Una implementación de C# debe cumplir con el estándar, independientemente del modelo de procesador de destino. Si el procesador no proporciona instrucciones correspondientes a los requisitos de C#, entonces la implementación C# debe entregar los resultados requeridos de alguna otra forma, ya sea mediante el uso de instrucciones nativas para la mayor parte del trabajo, pero "arreglando" los resultados con pruebas e instrucciones adicionales o emulando las operaciones de coma flotante a un nivel de bit, lo que sea necesario. Además, a diferencia de C, C# especifica rangos específicos para int y float. –

+0

@EricPostpischil Float también, ¿eh? Gracias por la caída del conocimiento! –

5

Al comparar un int y un float, el int se convierte implícitamente en un flotante. Esto garantiza que ocurra la misma pérdida de precisión, por lo que la comparación siempre será cierta. Mientras no perturbes el lanzamiento implícito o la aritmética, la igualdad debería mantenerse. Por ejemplo, si se escribe esto:

bool AlwaysTrue(int i) { 
    return i == (float)i; 
} 

hay una conversión implícita, por lo que es equivalente a esta función que siempre debe devolver verdadero:

bool AlwaysTrue(int i) { 
    return (float)i == (float)i; 
} 

pero si usted escribe esto:

bool SometimesTrue(int i) { 
    return i == (int)(float)i; 
} 

luego no hay más yeso implícito y la pérdida de precisión solo ocurre en el lado derecho. El resultado puede ser falso. Del mismo modo, si escribe esto:

bool SometimesTrue(int i) { 
    return 1 + i == 1 + (float)i; 
} 

entonces la pérdida de precisión podría no ser equivalente en ambos lados. El resultado puede ser falso.

+0

Sí, gracias por señalar que lo contrario no siempre es cierto. También ese range-case con la adición es divertido. –

Cuestiones relacionadas