Quando si confrontano valori in virgola mobile per l'uguaglianza, esistono due approcci diversi:
NaN
non essere uguale a se stesso, che corrisponde alla specifica IEEE 754 .NaN
essendo uguale a se stesso, che fornisce la proprietà matematica della Riflessività che è essenziale per la definizione di una relazione di equivalenza
I tipi in virgola mobile IEEE integrati in C # ( float
e double
) seguono la semantica IEEE per ==
e !=
(e gli operatori relazionali gradiscono <
) ma assicurano la riflessività per object.Equals
, IEquatable<T>.Equals
(e CompareTo
).
Consideriamo ora una libreria che fornisce strutture vettoriali in cima a float
/ double
. Un tale tipo di vettore sovraccaricherebbe ==
/ !=
e sovrascriverà object.Equals
/ IEquatable<T>.Equals
.
Ciò su cui tutti concordano è che ==
/ !=
dovrebbero seguire la semantica IEEE. La domanda è: se tale libreria implementasse il Equals
metodo (che è separato dagli operatori di uguaglianza) in un modo che è riflessivo o in un modo che corrisponde alla semantica IEEE.
Argomenti per l'utilizzo della semantica IEEE per Equals
:
- Segue IEEE 754
È (forse molto) più veloce perché può trarre vantaggio dalle istruzioni SIMD
Ho fatto una domanda separata su StackOverflow su come esprimere l'uguaglianza riflessiva usando le istruzioni SIMD e il loro impatto sulle prestazioni: istruzioni SIMD per il confronto dell'uguaglianza in virgola mobile
Aggiornamento: sembra che sia possibile implementare in modo efficiente l'uguaglianza riflessiva usando tre istruzioni SIMD.
La documentazione per
Equals
non richiede riflessività quando si tratta di virgola mobile:Le seguenti istruzioni devono essere vere per tutte le implementazioni del metodo Equals (Object). Nell'elenco,
x
,y
, ez
rappresentano riferimenti a oggetti che non sono nulla.x.Equals(x)
restituiscetrue
, tranne nei casi che coinvolgono tipi in virgola mobile. Vedi ISO / IEC / IEEE 60559: 2011, Tecnologia dell'informazione - Sistemi a microprocessore - Aritmetica a virgola mobile.Se stai usando i float come chiavi del dizionario, stai vivendo in uno stato di peccato e non dovresti aspettarti un comportamento sano.
Argomenti per essere riflessivo:
E 'coerente con i tipi esistenti, tra cui
Single
,Double
,Tuple
eSystem.Numerics.Complex
.Non conosco alcun precedente nel BCL in cui
Equals
segue IEEE invece di essere riflessivo. Esempi includono contatoriSingle
,Double
,Tuple
eSystem.Numerics.Complex
.Equals
viene utilizzato principalmente da contenitori e algoritmi di ricerca che si basano sulla riflessività. Per questi algoritmi un guadagno in termini di prestazioni è irrilevante se impedisce loro di funzionare. Non sacrificare la correttezza per le prestazioni.- Si rompe tutti i set e dizionari hash basato,
Contains
,Find
,IndexOf
su varie collezioni / LINQ, le operazioni di set LINQ base (Union
,Except
, ecc) se i dati contengonoNaN
valori. Il codice che esegue calcoli effettivi in cui è accettabile la semantica IEEE di solito funziona su tipi e usi concreti
==
/!=
(o più probabilmente confronti epsilon).Al momento non è possibile scrivere calcoli ad alte prestazioni utilizzando i generici poiché sono necessarie operazioni aritmetiche, ma questi non sono disponibili tramite interfacce / metodi virtuali.
Quindi un
Equals
metodo più lento non influirebbe sulla maggior parte del codice ad alte prestazioni.È possibile fornire un
IeeeEquals
metodo o unoIeeeEqualityComparer<T>
per i casi in cui è necessaria la semantica IEEE o è necessario un vantaggio in termini di prestazioni.
A mio avviso, questi argomenti favoriscono fortemente un'implementazione riflessiva.
Il team CoreFX di Microsoft prevede di introdurre un tale tipo di vettore in .NET. A differenza di me , preferiscono la soluzione IEEE , principalmente a causa dei vantaggi prestazionali. Dal momento che una tale decisione non sarà certamente cambiata dopo una versione finale, voglio ottenere un feedback dalla community, su quello che credo sia un grosso errore.
float
/ double
e molti altri tipi, ==
e Equals
sono già diversi. Penso che un'incongruenza con i tipi esistenti sarebbe ancora più confusa rispetto all'incongruenza tra ==
e Equals
dovrai comunque occuparti di altri tipi. 2) Praticamente tutti gli algoritmi / raccolte generici usano Equals
e si basano sulla sua riflessività per funzionare (LINQ e dizionari), mentre gli algoritmi concreti in virgola mobile usano tipicamente ==
dove ottengono la loro semantica IEEE.
Vector<float>
una "bestia" diversa da una semplice float
o double
. Con questa misura, non riesco a vedere il motivo Equals
o l' ==
operatore di rispettarne gli standard. Ti sei detto: "Se stai usando i float come chiavi del dizionario, stai vivendo in uno stato di peccato e non dovresti aspettarti un comportamento sano". Se uno dovesse archiviare NaN
in un dizionario, allora è colpa sua maledetta per l'uso della pratica terribile. Non credo che il team CoreFX non ci abbia pensato. Vorrei andare con un ReflexiveEquals
o simile, solo per motivi di prestazioni.
==
eEquals
restituirebbe risultati diversi. Molti programmatori credono di si e fanno la stessa cosa . Inoltre - in generale, le implementazioni degli operatori di uguaglianza invocano ilEquals
metodo. Hai sostenuto che si potrebbe includere unIeeeEquals
, ma si potrebbe anche fare il contrario e includere unReflexiveEquals
metodo. IlVector<float>
tipo può essere utilizzato in molte applicazioni critiche per le prestazioni e deve essere ottimizzato di conseguenza.