`Vektör <float> .Equals` dönüşlü olmalı mı yoksa IEEE 754 anlambilimine uygun mu?


9

Eşitlik için kayan nokta değerleri karşılaştırılırken iki farklı yaklaşım vardır:

C # ( floatve double) içindeki yerleşik IEEE kayan nokta türleri ve ==ve !=(ve ilişkisel operatörler gibi <) için IEEE semantiğini takip eder, ancak object.Equals, IEquatable<T>.Equals(ve CompareTo) için yansıma sağlar .

Şimdi float/ üstünde vektör yapılar sağlayan bir kütüphane düşünün double. Böyle bir vektör tipi aşırı yüklenir ==/ !=ve geçersiz kılınır object.Equals/ IEquatable<T>.Equals.

Herkesin kabul ettiği şey, IEEE anlambilimine uymak ==/ !=uymaktır. Soru, böyle bir kütüphanenin Equalsyöntemi (eşitlik operatörlerinden ayrı olarak) refleksif veya IEEE anlambilimine uygun bir şekilde uygulamasıdır.

IEEE semantiğini aşağıdakiler için kullanmak için bağımsız değişkenler Equals:

  • IEEE 754'ü takip ediyor
  • SIMD talimatlarından yararlanabileceğinden (muhtemelen çok daha hızlı)

    Stackoverflow hakkında, SIMD talimatlarını ve performans etkilerini kullanarak refleksif eşitliği nasıl ifade edeceğiniz hakkında ayrı bir soru sordum: Kayan nokta eşitliği karşılaştırması için SIMD talimatları

    Güncelleme: Üç SIMD talimatını kullanarak refleksif eşitliği etkili bir şekilde uygulamak mümkün görünmektedir.

  • İçin dokümantasyon Equals, kayan noktayı dahil ederken refleksivite gerektirmez:

    Aşağıdaki ifadeler, Equals (Object) yönteminin tüm uygulamaları için doğru olmalıdır. Listede, x, yve zboş olmayan nesne referanslarını temsil eder.

    x.Equals(x)truekayan noktalı türleri içeren durumlar dışında döner . Bkz. ISO / IEC / IEEE 60559: 2011, Bilgi teknolojisi - Mikroişlemci Sistemleri - Kayan Nokta aritmetiği.

  • Sözlük anahtarı olarak şamandıralar kullanıyorsanız, günah durumunda yaşıyorsunuz ve aklı başında davranış beklememelisiniz.

Dönüşlü olma argümanları:

  • Bu da dahil olmak üzere, mevcut tipleri, uyuyor Single, Double, Tupleve System.Numerics.Complex.

    BCL'de Equalsrefleksif olmak yerine IEEE'yi takip eden herhangi bir örnek bilmiyorum . Sayaç örnekler Single, Double, Tupleve System.Numerics.Complex.

  • Equalsçoğunlukla refleksiviteye dayanan kapsayıcılar ve arama algoritmaları tarafından kullanılır. Bu algoritmalar için, bir performans kazancı, çalışmalarını önlüyorsa önemsizdir. Performans için doğruluktan ödün vermeyin.
  • Tüm karma tabanlı setleri ve sözlükleri kırar, Contains, Find, IndexOfçeşitli koleksiyonlardan / LINQ set bazlı LINQ operasyonları (üzerinde Union, Exceptvs.) veri içeriyorsa NaNdeğerlerini.
  • IEEE semantiğinin kabul edilebilir olduğu gerçek hesaplamaları yapan kod genellikle somut türler üzerinde çalışır ve ==/ !=(veya daha olası epsilon karşılaştırmaları) kullanır.

    Bunun için aritmetik işlemlere ihtiyaç duyduğunuzdan, şu anda jenerikler kullanarak yüksek performanslı hesaplamalar yazamazsınız, ancak bunlar arayüzler / sanal yöntemler aracılığıyla kullanılamaz.

    Bu nedenle, daha yavaş bir Equalsyöntem çoğu yüksek performans kodunu etkilemez.

  • IEEE semantiğine veya performans avantajına ihtiyaç duyduğunuz durumlarda bir IeeeEqualsyöntem veya bir sağlamak mümkündür IeeeEqualityComparer<T>.

Kanımca bu argümanlar dönüşlü bir uygulamayı kuvvetle desteklemektedir.

Microsoft'un CoreFX ekibi böyle bir vektör türünü .NET'e tanıtmayı planlıyor. Benden farklı olarak , temel olarak performans avantajları nedeniyle IEEE çözümünü tercih ediyorlar . Son bir sürümden sonra böyle bir karar kesinlikle değiştirilmeyeceğinden, büyük bir hata olduğuna inandığım şeyden topluluktan geri bildirim almak istiyorum.


1
Mükemmel ve düşünce provoke soru. Benim için (en azından), bunun bir anlamı yok ==ve Equalsfarklı sonuçlar getirecekti. Birçok programcı olduklarını varsayalım ve bunu aynı şeyi . Ayrıca - genel olarak, eşitlik operatörlerinin uygulamaları Equalsyöntemi çağırmaktadır . Birinin bir içerebileceğini iddia ettiniz IeeeEquals, ancak biri bunu başka şekilde de yapabilir ve bir- ReflexiveEqualsyöntem içerebilir . Vector<float>-Tipi kullanılabilen çok performans açısından kritik uygulamalar ve buna göre optimize edilmelidir.
ölmek maus

@diemaus Bu inandırıcı bulmamın bazı nedenleri: 1) float/ doubleve diğer bazı türler için ==ve Equalszaten farklı. Mevcut türlerle tutarsızlığın, aralarındaki tutarsızlıktan daha da kafa karıştırıcı olacağını düşünüyorum ==ve Equalsyine de diğer türlerle uğraşmak zorunda kalacaksınız. 2) Hemen hemen tüm jenerik algoritmalar / koleksiyonlar Equalsişlevini (LINQ ve sözlükler) kullanırlar ve somut kayan nokta algoritmaları tipik olarak ==IEEE semantiklerini aldıkları yerde kullanırlar .
CodesInChaos

Ben Vector<float>basit floatya da farklı bir "canavar" düşünün double. Bu önlemle, operatörün standartlarına uyma nedenini Equalsveya ==operatörünü göremiyorum . Kendinizi dediniz: "Eğer sözlük anahtarları olarak kayan nokta kullanıyorsanız, günah durumunda yaşıyorsunuz ve aklı başında davranış beklememelisiniz". Biri NaNsözlükte saklanacak olsaydı , o zaman korkunç bir uygulama kullanmak kendi kahrolası hatasıdır. CoreFX ekibinin bunu düşünmediğini pek düşünmüyorum. ReflexiveEqualsSadece performans uğruna bir veya benzeri ile giderdim .
ölmek maus

Yanıtlar:


5

IEEE davranışının doğru olduğunu iddia ediyorum. NaNs hiçbir şekilde birbirine eşdeğer değildir; sayısal bir cevabın uygun olmadığı kötü tanımlanmış koşullara karşılık gelirler.

Çoğu işlemcinin doğal olarak desteklediği IEEE aritmetiği kullanmanın getirdiği performans avantajlarının ötesinde, eğer varsa isnan(x) && isnan(y), o zaman semantik bir sorun olduğunu düşünüyorum x == y. Örneğin:

// C++
double inf = std::numeric_limits<double>::infinity();
double x = 0.0 / 0.0;
double y = inf - inf;

Birisinin xeşit olarak düşünmesinin anlamlı bir nedeni olmadığını iddia ediyorum y. Eşdeğer sayılar olduğu sonucuna varamazsınız; bunlar sayı değil, bu yüzden tamamen geçersiz bir kavram gibi görünüyor.

Ayrıca, bir API tasarım perspektifinden bakıldığında, birçok programcı tarafından kullanılması amaçlanan genel amaçlı bir kitaplık üzerinde çalışıyorsanız, en endüstriye özgü kayan nokta semantiğini kullanmak mantıklıdır. İyi bir kütüphanenin amacı, onu kullananlar için zamandan tasarruf etmektir, bu nedenle standart dışı davranışlarda bina kafa karışıklığı için olgunlaşır.


3
Yani NaN == NaNdönmelidir sahte tartışılmazdır. Soru, .Equalsyöntemin ne yapması gerektiğidir. Örneğin NaN, bir sözlük anahtarı olarak kullanırsam , NaN.Equals(NaN)return false olursa ilişkili değer geri alınamaz hale gelir .
CodesInChaos

1
Genel durum için optimizasyon yapmanız gerektiğini düşünüyorum. Bir sayı vektörü için genel durum, yüksek verimli sayısal hesaplamadır (genellikle SIMD talimatları ile optimize edilir). Bir vektörü sözlük anahtarı olarak kullanmanın son derece nadir bir kullanım örneği olduğunu ve anlambiliminizi tasarlamaya değmeyeceğini iddia ediyorum. Varolan beri benim için en makul görünüyor karşı getirdiği, kıvam olduğu Single, Doublevb sınıfları zaten dönüşlü davranışa sahip. IMHO, bu sadece yanlış bir karardı. Ancak zarafetin yararlılık / hıza girmesine izin vermem.
Jason R

Ancak tipik olarak ==her zaman IEEE'yi takip eden sayısal hesaplamalar kullanılır , böylece nasıl Equalsuygulanırsa uygulansın hızlı kodu alırlar . IMO, EqualsLINQ'nun Distinct()işlevi gibi, beton tipini umursamayan algoritmalarda ayrı bir yönteme sahip olmanın tüm amacıdır .
CodesInChaos

1
Anladım. Ama bir ==operatöre ve Equals()farklı semantiğe sahip bir işleve sahip bir API'ye karşı tartışırım . Sanırım gerçek bir faydası olmayan bir geliştirici perspektifinden potansiyel karışıklık maliyeti ödüyorsunuz (bir sayı vektörünü sözlük anahtarı olarak kullanabilmek için herhangi bir değer atamıyorum). Bu sadece benim görüşüm; Eldeki soruya objektif bir cevap olduğunu sanmıyorum.
Jason R

0

Bir sorun var: IEEE754 ilişkisel işlemleri ve eşitliği sayısal uygulamalara uygun şekilde tanımlar. Sıralama ve hashlama için uygun değildir. Bir diziyi sayısal değerlere göre sıralamak istiyorsanız veya bir kümeye sayısal değerler eklemek veya bunları sözlükte anahtar olarak kullanmak istiyorsanız, NaN değerlerine izin verilmediğini bildirirsiniz veya IEEE754 kullanmazsınız yerleşik işlemler. Karma tablonuz tüm NaN'lerin aynı değerle eşleştiğinden emin olmalı ve birbirleriyle eşit olmalıdır.

Vector öğesini tanımlarsanız, yalnızca sayısal amaçlar için kullanmak isteyip istemediğinize veya sıralama ve karma ile uyumlu olup olmayacağına dair tasarım kararı vermeniz gerekir. Şahsen sayısal amacın çok daha önemli olması gerektiğini düşünüyorum. Sıralama / karma işlemi gerekiyorsa, Vector ile bir sınıfı üye olarak yazabilir ve karma ve eşitliği o sınıfta istediğiniz gibi tanımlayabilirsiniz.


1
Sayısal amaçların daha önemli olduğuna katılıyorum. Ama onlar için zaten ==ve !=operatörlerimiz var . Deneyimlerime göre Equalsyöntem hemen hemen sadece sayısal olmayan algoritmalar tarafından kullanılmaktadır.
CodesInChaos
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.