Bir şamandırayı başka bir değişkene kopyalarsam eşit olur mu?


167

==Kayan nokta değişkenlerinin eşitliğini kontrol etmek için kullanmanın iyi bir yol olmadığını biliyorum . Ama bunu sadece aşağıdaki ifadelerle bilmek istiyorum:

float x = ...

float y = x;

assert(y == x)

Yana ykopyalanır x, iddia doğru olacak?


78
Gerçek kodlu bir gösteri ile aslında eşitsizliği kanıtlayan birine 50 lütuf vereyim. 80 vs 64 bitlik şeyleri görmek istiyorum. Artı başka bir 50 bir değişken bir kayıt ve diğer değil (veya eşitsizliğin nedeni ne olursa olsun, düşük düzeyde açıklamak istiyorum) olduğunu gösteren oluşturulan derleyici kodunun açıklaması için.
Thomas Weller

1
@ThomasGCC hatası bu konuda: gcc.gnu.org/bugzilla/show_bug.cgi?id=323 ; Ancak, ben sadece bir x86-64 sistemi üzerinde repro denedim ve -ffast-math ile bile değil. 32 bit sistemde eski bir GCC'ye ihtiyacınız olduğundan şüpheleniyorum.
pjc50

5
@ pjc50: Aslında hata 323'ü yeniden üretmek için 80 bitlik bir sisteme ihtiyacınız var; soruna neden olan 80x87 FPU. x86-64, SSE FPU'yu kullanır. Ekstra bitler soruna neden olur, çünkü bir değeri 32 bitlik bir yüzdeye dökerken yuvarlanırlar.
MSalters

4
MSalters'ın teorisi doğruysa (ve bundan şüpheliyim), 32 bit ( -m32) için derleyerek veya GCC'ye x87 FPU ( -mfpmath=387) kullanmasını söyleyerek çoğaltabilirsiniz .
Cody Gray

4
"48 bit" i "80 bit" olarak değiştirin ve sonra "efsanevi" sıfatı @Hot'tan kaldırabilirsiniz. Yorumunuzdan hemen önce tartışılan şey tam olarak bu oldu. X87 (x86 mimarisi için FPU), "geniş kapsamlı" bir biçim olan 80 bit kayıtlar kullanır.
Cody Gray

Yanıtlar:


125

assert(NaN==NaN);Kmdreko tarafından işaret edilen davanın yanı sıra, 80 bit şamandıralar geçici olarak hafızaya kaydedildiğinde ve daha sonra hala bir sicilde saklanan değerlerle karşılaştırıldığında x87-math ile durumlarınız olabilir.

Aşağıdakilerle derlendiğinde gcc9.2 ile başarısız olabilecek olası en az örnek -O2 -m32:

#include <cassert>

int main(int argc, char**){
    float x = 1.f/(argc+2);
    volatile float y = x;
    assert(x==y);
}

Godbolt Demosu: https://godbolt.org/z/X-Xt4R

volatileYeterli kayıt-basınç oluşturmak için yönetmek durumunda muhtemelen etmiş, atlanabilir ysaklanabilir ve bellekten yeniden (ama derleyici yeterli, hepsi bir arada karşılaştırma ihmal edilmeyip karıştırmayın).

GCC SSS referansına bakın:


2
Ekstra bitlerin floatstandart hassasiyetle ekstra hassasiyet karşılaştırılmasında dikkate alınması garip görünüyor .
Nat

13
Bu @Nat olduğu garip; bu bir hatadır .
Orbit'te Hafiflik Yarışları

13
@ThomasWeller Hayır, bu makul bir ödül. Ben bunun cevabının uyumsuz davranış olduğuna işaret etmek istiyorum
Orbit'teki Lightness Races

4
Bu cevabı, montaj kodunda tam olarak ne olduğunu ve bu standardı ihlal ettiğini belirterek uzatabilirim - kendime bir dil avukatı demesem de, bu yüzden belirsiz olmadığını garanti edemem bu davranışa açıkça izin veren madde. OP'nin tamamen dertsiz, tamamen uyumlu derleyicilerle değil, gerçek derleyicilerdeki pratik komplikasyonlarla daha fazla ilgilendiğini varsayıyorum (ki fiili yok, sanırım).
chtz

4
Bahsetmeye değer -ffloat-storebunu önlemek için bir yol gibi görünüyor.
OrangeDog

116

Bu true olmayacaktır xolan NaNkarşılaştırmalar üzerinde beri NaNvardır her zaman false (evet, hatta NaN == NaN). Diğer tüm durumlar için (normal değerler, alt normal değerler, sonsuzluklar, sıfırlar) bu iddia doğru olacaktır.

Kayan noktalardan kaçınma tavsiyesi , kayan nokta sayılarının aritmetik ifadelerde kullanıldığında birçok sonucu tam olarak ifade edememesi nedeniyle yapılan hesaplamalar== için geçerlidir . Atama bir hesaplama değildir ve atamanın orijinalden farklı bir değer vermesinin bir nedeni yoktur.


Standartlara uyulursa, genişletilmiş hassasiyetli bir sorun söz konusu olmamalıdır. Kaynaktan <cfloat>C miras [5.2.4.2.2.8] ( vurgu mayın ):

Atama ve yayın hariç (tüm ekstra aralığı ve hassasiyeti kaldıran) , kayan işlenenlerle işlemlerin değerleri ve normal aritmetik dönüşümlere ve kayan sabitlere tabi değerler, aralığı ve hassasiyeti, yazın.

Comments belirttiğimiz gibi Ancak, bazı derleyici, yap-seçenekleri ve hedefleri olan bazı vakalar olabilir bu paradoksal yanlış yapmak.


10
Ya xbir için minimum tutardan fazla hassasiyet tutarak ilk satırda bir kayıtta hesaplanır float. y = xSadece tutarak bellekte olabilir floathassasiyet. Daha sonra eşitlik testi, sicile karşı bellekle, farklı kesinliklerle ve dolayısıyla hiçbir garanti olmadan yapılacaktır.
David Schwartz

5
x+pow(b,2)==x+pow(a,3)auto one=x+pow(b,2); auto two=y+pow(a,3); one==twobiri diğerinden daha fazla kesinlik kullanarak karşılaştırabileceğinden farklı olabilir (eğer bir / iki ram'da 64 bit değerler ise, intermediste değerleri fpu üzerinde 80ish bit ise). Böylece ödev bazen bir şeyler yapabilir.
Yakk - Adam Nevraumont

22
@evg Elbette! Cevabım sadece standardı takip ediyor. Derleyicinize, özellikle hızlı matematiği etkinleştirirken, kafa karıştırıcı olmadığını söylerseniz, tüm bahisler kapalıdır.
kmdreko

11
@Voo Cevabımda alıntıya bakın. RHS değeri LHS üzerindeki değişkene atanır. LHS'nin sonuç değerinin RHS değerinden farklı olması için yasal bir gerekçe yoktur. Birçok derleyicinin bu konuda hatalar olduğunu takdir ediyorum. Ancak bir şeyin bir kayıtta depolanıp depolanmadığı, onunla hiçbir ilgisi yoktur.
Orbit'te Hafiflik Yarışları

6
@Voo: ISO C ++ 'da, herhangi bir atamada tür genişliğine yuvarlama yapılması gerekir. X87'yi hedefleyen çoğu derleyicide, bu yalnızca derleyici dökülmeye / yeniden yüklenmeye karar verdiğinde gerçekleşir. gcc -ffloat-storeSıkı uyum için zorlayabilirsiniz . Ancak bu soru, x=y; x==y; ikisi arasında değişiklik yapmak için hiçbir şey yapmadan ilgilidir. Eğer yzaten iki katına çıkması veya uzun çift ve arka değerini değişmeyecek dönüştürerek, Bir tayin sığacak şekilde yuvarlanır. ...
Peter Cordes

34

Evet, ykesinlikle değerini alacaktır x:

[expr.ass]/2: Basit atamada (=), sol işlenen tarafından atıfta bulunulan nesne, değeri sağ işlenenin sonucuyla değiştirilerek değiştirilir ([defns.access]).

Diğer değerlerin atanması için boşluk yoktur.

(Diğerleri, bir denklik karşılaştırmasının ==yine de falseNaN değerleri için değerlendirileceğini belirtmişlerdir .)

Kayan nokta ile ilgili genel sorun, düşündüğünüz değere sahip ==olmanın kolay olmamasıdır . Burada iki değerin, her neyse, aynı olduklarını biliyoruz.


7
@ThomasWeller Sonuç olarak uyumlu olmayan bir uygulamada bilinen bir hata. Olsa da bahsetmek güzel!
Orbit'te Hafiflik Yarışları

İlk başta, "değer" ve "sonuç" arasındaki ayrımı avlayan dilin sapkın olacağını düşündüm, ancak bu ayrımın C2.2, 7.1.6 dilinden farksız olması gerekmiyordu; C3.3, 7.1.6; Belirttiğiniz taslak Standardın C4.2, 7.1.6 veya C5.3, 7.1.6.
Eric Towers

@EricTowers Maalesef bu referansları netleştirebilir misiniz? Ne demek istediğini bulamıyorum
Orbit'teki Lightness Races

@ LightnessRacesBY-SA3.0: C . C2.2 , C3.3 , C4.2 ve C5.3 .
Eric Towers

@EricTowers Evet, hala seni takip etmiyorum. İlk bağlantınız Ek C dizinine gider (bana hiçbir şey söylemez). Sonraki dört bağlantının hepsi şuraya gider [expr]. Bağlantıları görmezden gelir ve alıntılara odaklanırsam, örneğin C.5.3'ün "değer" veya "sonuç" terimlerinin kullanımına değinmediği ( "sonucunu" normal İngilizce bağlamında bir kez kullanın). Belki de standardın nerede bir ayrım yaptığını düşündüğünüzü daha açık bir şekilde açıklayabilir ve bu olay için tek bir açık alıntı yapabilirsiniz. Teşekkürler!
Yörüngedeki Hafiflik Yarışları

3

Evet, her durumda (NaN'leri ve x87 sorunlarını göz ardı ederek), bu doğru olacaktır.

Eğer bir yaparsanız memcmponlara NaN'ler ve sNaNs karşılaştırmak mümkün olurken sen eşitlik mümkün test olacak. Bu ayrıca derleyicinin değeri float80 bit yerine 32 bit'e zorlayacak değişkenin adresini almasını gerektirir . Bu, x87 sorunlarını ortadan kaldıracaktır. Buradaki ikinci iddia, ==NaN'leri doğru olarak karşılaştırmayacağını göstermeme amaçlıdır:

#include <cmath>
#include <cassert>
#include <cstring>

int main(void)
{
    float x = std::nan("");
    float y = x;
    assert(!std::memcmp(&y, &x, sizeof(float)));
    assert(y == x);
    return 0;
}

NaN'lerin farklı bir iç temsili varsa (yani farklı mantis), bunun memcmpdoğru karşılaştırmayacağını unutmayın.


1

Normal durumlarda, doğru olarak değerlendirilir. (veya iddia beyanı hiçbir şey yapmaz)

Düzenle :

'Olağan durumlar' ile kastedilen, diğer kullanıcıların işaret ettiği gibi yukarıda belirtilen senaryoları (NaN değerleri ve 80x87 kayan nokta birimleri gibi) hariç tutuyorum.

Bugünün bağlamında 8087 yongaların eskimesi göz önüne alındığında, konu oldukça izole edilmiştir ve sorunun kullanılan kayan nokta mimarisinin mevcut durumuna uygulanabilir olması için, bu durum NaN'ler dışındaki tüm durumlar için geçerlidir.

(8087 hakkında referans - https://home.deec.uc.pt/~jlobo/tc/artofasm/ch14/ch143.htm )

Kudos, iyi bir örnek oluşturduğu için @chtz'e ve NaN'lerden bahsettiği için @kmdreko'ya - daha önce bunları bilmiyordu!


1
Bellekten yüklenirken xbir kayan nokta kaydında olmanın tamamen mümkün olduğunu düşündüm y. Bellek bir kayıttan daha az kesinlik kazanarak karşılaştırmanın başarısız olmasına neden olabilir.
David Schwartz

1
Bu yanlış bir durum olabilir, ben o kadar düşünmemiştim. (OP herhangi bir özel durum sağlamadığı için, ek kısıtlama kabul
etmiyorum

1
Ne dediğini gerçekten anlamıyorum. Soruyu anladığım gibi, OP bir şamandıranın kopyalanıp kopyalanmadığını soruyor ve daha sonra eşitlik testinin başarılı olacağı garanti ediliyor. Cevabınız "evet" diyor gibi görünüyor. Cevabın neden hayır olmadığını soruyorum.
David Schwartz

6
Düzenleme bu yanıtı yanlış yapar. C ++ standardı, atamanın değeri hedef türüne dönüştürmesini gerektirir; ifade değerlendirmelerinde aşırı duyarlık kullanılabilir, ancak atama yoluyla alıkonmayabilir. Değerin bir kayıt defterinde mi yoksa bellekte mi tutulması önemsizdir; C ++ standardı, kod yazıldığı gibi, floatfazla hassas olmayan bir değer olmasını gerektirir.
Eric Postpischil

2
@AProgrammer Bir (n derece) buggy derleyicisinin teorik olarak int a=1; int b=a; assert( a==b );bir iddia atmasına neden olabileceği düşünüldüğünde, bu soruyu sadece doğru çalışan bir derleyiciye göre cevaplamanın mantıklı olduğunu düşünüyorum (muhtemelen bazı derleyicilerin bazı sürümlerinin yapıldığını / -bunu bilinen-yanlış anlamak için). Pratik olarak, bir nedenden dolayı bir derleyici, kaydedilmiş bir ödevin sonucundaki ekstra hassasiyeti kaldırmazsa, bu değeri kullanmadan önce yapması gerekir .
TripeHound

-1

Evet, NaN olmadığı sürece her zaman True döndürür . Değişken değeri NaN ise, her zaman False !

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.