2011-02-25 18 views
44

This stackoverflow question planteó una pregunta interesante sobre la ordenación de matrices dobles con valores NaN. El PO ha publicado el siguiente código:¿Por qué .NET 4.0 ordena esta matriz de forma diferente que .NET 3.5?

static void Main(string[] args) 
{ 
    double[] someArray = { 4.0, 2.0, double.NaN, 1.0, 5.0, 3.0, double.NaN, 10.0, 9.0, 8.0 }; 

    foreach (double db in someArray) 
    { 
     Console.WriteLine(db); 
    } 

    Array.Sort(someArray); 
    Console.WriteLine("\n\n"); 
    foreach (double db in someArray) 
    { 
     Console.WriteLine(db); 
    } 

    Console.ReadLine(); 
} 

Cuando se ejecuta este en el marco .NET 3.5, la matriz se ordenan de la siguiente manera:

1,4,NaN,2,3,5,8,9,10,NaN

Cuando se ejecuta bajo .NET 4.0, la matriz se ordena un poco más lógica:

NaN,NaN,1,2,3,4,5,8,9,10

puedo entender por qué lo haría en una especie extraña .NET 3.5 (porque NaN no es igual a, menor que, o mayor que, nada). También puedo entender por qué se ordenaría de la forma en que lo hace en .NET 4.0. Mi pregunta es, ¿por qué esto cambió de 3.5 a 4.0? ¿Y dónde está la documentación de Microsoft para este cambio?

+2

sospecho que no hay una buena razón; probablemente solo un descuido de Microsoft. – Jacob

+3

Sé que arreglaron un par de cosas aquí, por ejemplo, hubo un error con tipos de cadenas no reflectivas, pero no tengo citas, solo ejemplos. –

+1

Sorprendentemente, [Lo nuevo en .NET 4.0] (http://msdn.microsoft.com/en-us/library/ms171868.aspx) no tiene ninguna mención de este cambio en particular. –

Respuesta

37

Es una corrección de errores. El informe de comentarios con los detalles del error is here.La respuesta de Microsoft al informe de fallo:

Tenga en cuenta que este error afecta a los siguientes:

  • Array.Sort(), donde la matriz contiene Double.NaN
  • Array.Sort(), donde la matriz contiene Single.NaN
  • cualquier personas que llaman de arriba, por ejemplo en list.sort(), donde lista contiene Double.NaN

Este error se corrigió en el próxima versión principal del tiempo de ejecución; hasta entonces puede solucionar esto utilizando un IComparer personalizado que realice la clasificación correcta. Como se menciona en los comentarios de la solución alternativa, no use Comparer.Default, ya que se trata de una función especial con una rutina de ordenación de acceso directo que no maneja NaN correctamente. En cambio, puede proporcionar su propio comparador que proporcione una comparación equivalente, pero no será especial.

2

No tengo el código para el tiempo de ejecución .NET 3.5 para verificar esto, pero creo que arreglaron un error en el comparador predeterminado para double para ponerlo en línea con the documentation.

De acuerdo con ese documento, Double.Compare trata NaN como igual a PositiveInfinity y NegativeInfinity, y menos de cualquier otro valor.

La documentación es la misma para .NET 3.5 y .NET 4.0, así que tengo que pensar que esto fue una corrección de errores para hacer que el código funcione como está documentado.

EDIT:

Después de leer los comentarios en la cuestión vinculada, tengo que pensar que el problema no estaba en Double.Compare, sino más bien en cualquier método Array.Sort (que es lo que depende de List.Sort) utiliza para comparar Double valores. De alguna manera, no creo que realmente sea Double.Compare.

2

[Esta es mi respuesta descarada arrancada de la otra publicación. Sería bueno que alguien es explorado más a fondo - double.CompareTo y double.CompareTo(double) están bien definidos, tal como se indica a continuación así que sospecho que hay algo mágico suceda Array.Sort para el tipo específico]

Array.Sort(double[]):. no parece estar usando CompareTo(double[]) como se esperaba y esto puede ser un error - observe la diferencia en Array.Sort (objeto []) y Array.Sort (doble []) debajo de. Me encantaría aclaraciones/correcciones sobre lo siguiente.

En primer lugar, la documentación double.CompareTo(T) método - esta ordenación está bien definido de acuerdo con la documentación:

Menos de cero: este caso es menor que el valor. -o-- Esta instancia no es un número (NaN) y el valor es un número.

Zero: Esta instancia es igual al valor. -o-- Tanto esta instancia como el valor no son un número (NaN), PositiveInfinity o NegativeInfinity.

Mayor que cero: este caso es mayor que el valor. -o-- Esta instancia es un número y el valor no es un número (NaN).

En LINQPad (3,5 y 4, ambos tienen los mismos resultados):

0d.CompareTo(0d).Dump();     // 0 
double.NaN.CompareTo(0d).Dump();   // -1 
double.NaN.CompareTo(double.NaN).Dump(); // 0 
0d.CompareTo(double.NaN).Dump();   // 1 

Usando CompareTo(object) tiene los mismos resultados:

0d.CompareTo((object)0d).Dump();     // 0 
double.NaN.CompareTo((object)0d).Dump();   // -1 
double.NaN.CompareTo((object)double.NaN).Dump(); // 0 
0d.CompareTo((object)double.NaN).Dump();   // 1 

Así que ese no es el problema.

Ahora, a partir de la documentación Array.Sort(object[]) - no hay uso de >, < o == (según la documentación) - sólo CompareTo(object).

Ordena los elementos en toda una matriz unidimensional utilizando el IComparable aplicación de cada elemento de la matriz.

Asimismo, Array.Sort(T[]) utiliza CompareTo(T).

Ordena los elementos de una matriz completa usando el IComparable (Of T) implementación de la interfaz genérica de cada elemento de la matriz.

Veamos:

LINQPad (4):

var ar = new double[] {double.NaN, 0, 1, double.NaN}; 
Array.Sort(ar); 
ar.Dump(); 
// NaN, NaN, 0, 1 

LINQPad (3.5):

var ar = new double[] {double.NaN, 0, 1, double.NaN}; 
Array.Sort(ar); 
ar.Dump(); 
// NaN, 0, NaN, 1 

LINQPad (3,5) - NOTA La matriz se DEL OBJETO y el comportamiento es "esperado" según el CompareTo contrato.

var ar = new object[] {double.NaN, 0d, 1d, double.NaN}; 
Array.Sort(ar); 
ar.Dump(); 
// NaN, NaN, 0, 1 

Hmm. De Verdad. En conclusión:

no tengo ni idea - pero yo sospecho hay algo de "optimización", resultando en CompareTo(double) no se invoque.

Happy coding.

+0

Esto es correcto. El operador '<' del lenguaje C# no es un orden total debido a su comportamiento 'NaN'. No puede usarse con algoritmos de clasificación por ese motivo. El método 'double.CompareTo' (y la implementación' double' de 'IComparable' y' IComparable <> 'da un orden total" bueno ". La instancia 'Comparer .Default' también obtiene este" buen "pedido. De acuerdo con la documentación para .NET 3.5 y .NET 4.0, el método 'Sort 'debe usar el algoritmo bueno. Al igual que usted especificó, .NET 3.5 tuvo una "optimización" para algunos tipos, incluyendo 'double', que" olvidó "manejar' NaN's. –

6
No

realmente una respuesta, pero quizás una pista ... Puede reproducir el comportamiento extraño 3.5 en 4.0 con este código:

void Main() 
{ 
    double[] someArray = { 4.0, 2.0, double.NaN, 1.0, 5.0, 3.0, double.NaN, 10.0, 9.0, 8.0 }; 
    Array.Sort(someArray, CompareDouble); 
    someArray.Dump(); 
} 

int CompareDouble(double a, double b) 
{ 
    if (a > b) 
     return 1; 
    if (a < b) 
     return -1; 
    return 0; 
} 

Aquí, tanto a > b y a < b retorno falso si aob es NaN, por lo que el método CompareDouble devuelve 0, por lo NaN se considera igual a todo lo ... Esto le da el mismo resultado que en el punto 3.5:

1,4,NaN,2,3,5,8,9,10,NaN 
+0

+1 Realmente, todo se trata de valores de parámetros predeterminados (de manera predeterminada, 3.5 está ordenando usando CompareDouble), en 4.0, la situación se ha mejorado –

Cuestiones relacionadas