C ++ uyarısı: çiftin sıfıra bölünmesi


98

Dava 1:

#include <iostream>

int main()
{
    double d = 15.50;
    std::cout<<(d/0.0)<<std::endl;
}

Herhangi bir uyarı ve baskı olmadan derlenir inf. Tamam, C ++ sıfıra bölmeyi işleyebilir ( canlı görün ).

Fakat,

Durum 2:

#include <iostream>

int main()
{
    double d = 15.50;
    std::cout<<(d/0)<<std::endl;
}

Derleyici aşağıdaki uyarıyı verir ( canlıya bakın ):

warning: division by zero [-Wdiv-by-zero]
     std::cout<<(d/0)<<std::endl;

Derleyici neden ikinci durumda bir uyarı veriyor?

0 != 0.0?

Düzenle:

#include <iostream>

int main()
{
    if(0 == 0.0)
        std::cout<<"Same"<<std::endl;
    else
        std::cout<<"Not same"<<std::endl;
}

çıktı:

Same

9
İkinci durumda tamsayı olarak sıfırı aldığını ve hesaplama daha sonra double kullanılarak yapılsa bile bir uyarı düşürdüğünü varsayıyorum (ki bence d double olduğunda davranış olmalı).
Qubit

10
Bu bir QoI sorunu, gerçekten. Ne uyarı ne de uyarı eksikliği, C ++ standardının kendisi tarafından zorunlu kılınan bir şey değildir. GCC kullanıyor musunuz?
StoryTeller - Unslander Monica


5
@StoryTeller QoI nedir? en.wikipedia.org/wiki/QoI ?
user202729

5
Son sorunuzla ilgili olarak, "0, 0.0 ile aynı mıdır?" Cevap, değerler aynıdır, ancak sizin de öğrendiğiniz gibi, bu onların aynı oldukları anlamına gelmez. Farklı şekiller! Tıpkı "A" nın 65 ile aynı olmaması gibi.
Bay Lister

Yanıtlar:


108

Kayan nokta bölme sıfıra göre IEEE tarafından iyi tanımlanır ve sonsuzluk verir (pay değerine göre pozitif veya negatif (veya NaN± 0 için) ).

Tamsayılar için sonsuzluğu temsil etmenin bir yolu yoktur ve dil işlemi tanımsız davranışa sahip olacak şekilde tanımlar, böylece derleyici sizi bu yoldan uzaklaştırmaya yardımcı olur.

Ancak bu durumda, pay a doubleolduğu için bölen ( 0) de ikiye yükseltilmelidir ve burada uyarı vermemekle birlikte burada uyarı vermek için bir neden yok, 0.0bu yüzden bunun bir derleyici hatası olduğunu düşünüyorum.


8
Her ikisi de kayan nokta bölümleri. In d/0, 0türüne dönüştürülür d.

43
IEEE 754 kullanmak için C ++ 'ın gerekli olmadığını unutmayın (farklı bir standart kullanan bir derleyici görmemiş olsam da) ..
Yksisarvinen

1
@hvd, iyi nokta, bu durumda bu bir derleyici hatası gibi görünüyor
Motti

14
Ya her iki durumda da uyarması gerektiğini ya da her iki durumda da uyarmaması gerektiğini kabul ediyorum (derleyicinin sıfıra göre kayan bölmeyi işlemesine bağlı olarak)
MM

8
Kayan nokta bölmesinin sıfıra bölünmesinin de UB olduğundan oldukça emin - sadece GCC bunu IEEE 754'e göre uyguluyor. Yine de bunu yapmak zorunda değiller.
Martin Bonner Monica'yı

42

Standart C ++ 'da, her iki durum da tanımsız davranıştır . Sabit sürücünüzü biçimlendirmek dahil her şey olabilir. "Geri dönüş tamam" veya başka herhangi bir davranış beklememeli veya bunlara güvenmemelisiniz.

Görünüşe göre derleyici bir durumda diğerinde değil bir uyarı vermeye karar veriyor, ancak bu bir kodun tamam olduğu ve birinin olmadığı anlamına gelmez. Bu, derleyicinin uyarıları oluşturmasının bir tuhaflığıdır.

C ++ 17 standardından [expr.mul] / 4:

İkili /operatör bölümü verir ve ikili %operatör, ilk ifadenin ikinciye bölünmesinden kalanı verir. İkinci işlenen /veya %sıfır ise, davranış tanımsızdır.


21
Doğru değil, kayan nokta aritmetiğinin sıfıra bölmesinde iyi tanımlanmıştır.
Motti

9
@Motti - Biri yalnızca C ++ standardıyla sınırlandırılırsa, böyle bir garanti yoktur. Açıkçası, bu sorunun kapsamı iyi belirtilmemiştir.
StoryTeller - Unslander Monica

9
Eğer ki (bunun için standart belgenin kendisi bakmadım gerçi) eminim @StoryTeller std::numeric_limits<T>::is_iec559olduğu trueiçin sıfıra ardından, bölünme TUB (ve değil bu kadar çoğu platformlarda trueiçin doubleve floattaşınabilir olması her ne kadar sen olacak, ifveya if constexpr) ile açıkça kontrol etmeniz gerekir .
Daniel H

6
@DanielH - "Makul" aslında oldukça özneldir. Bu soru dil avukatı olarak etiketlenmiş olsaydı, tamamen başka (çok daha küçük) bir dizi makul varsayım olurdu.
StoryTeller - Unslander Monica

5
@MM Unutmayın ki tam olarak söylediğiniz şey bu, ama daha fazlası değil: tanımsız, hiçbir koşulun empoze edilmediği anlamına gelmez, bu standart tarafından hiçbir koşulun getirilmediği anlamına gelir . Ben, bu durumda bir uygulama tanımlar gerçeğini iddia ediyorum is_iec559olarak truearaçlarının uygulanması standart yapraklar tanımlanmamış davranışı belgelemektedir. Sadece bu, uygulamanın belgelerinin programlı olarak okunabileceği bir durumdur. Tek değil: aynı şey is_moduloişaretli tam sayı türleri için de geçerlidir .

13

Cevaplamak için En iyi tahminim bu özel soru uyarı olduğunu derleyici yaydığı olurdu önce dönüşümünü gerçekleştirmek intüzere double.

Yani adımlar şu şekilde olacaktır:

  1. İfadeyi ayrıştır
  2. Aritmetik operatör /(T, T2) burada, T=double, T2=int.
  3. Emin olun std::is_integral<T2>::valueolduğunu trueve b == 0bu tetikleyiciler uyarı -.
  4. Uyarı vermek
  5. Örtülü dönüşüm gerçekleştirme T2içindouble
  6. İyi tanımlanmış bölme gerçekleştirin (derleyici IEEE 754 kullanmaya karar verdiğinden beri).

Bu elbette bir spekülasyondur ve derleyici tarafından tanımlanan spesifikasyonlara dayanmaktadır. Standart bakış açısından, olası Tanımlanmamış Davranışlarla uğraşıyoruz.


Lütfen bunun GCC belgelerine göre beklenen davranış olduğunu unutmayın
(btw. Bu bayrak GCC 8.1'de açıkça kullanılamıyor gibi görünüyor)

-Wdiv-by-sıfır
Derleme zamanında tamsayı sıfıra bölme konusunda uyar. Bu varsayılandır. Uyarı mesajlarını engellemek için -Wno-div-by-zero kullanın. Kayan noktayı sıfıra bölme, sonsuzlukları ve NaN'leri elde etmenin meşru bir yolu olabileceğinden, hakkında uyarılmaz.


2
Bu, C ++ derleyicilerinin çalışma şekli değildir. Derleyicinin, /bölündüğünü bilmek için aşırı yük çözümlemesi gerçekleştirmesi gerekir. Sol taraf bir Foonesne olsaydı ve bir de olurdu operator/(Foo, int), o zaman bölünme bile olmayabilir. Derleyici, yalnızca built-in / (double, double)sağ tarafın örtük dönüşümünü kullanmayı seçtiğinde bölündüğünü bilir . Ancak bu, bir bölme yapmadığı anlamına gelir int(0), bir bölme yapar double(0).
MSalters

@MSalters Lütfen buna bakın. C ++ hakkındaki bilgim sınırlıdır, ancak referansa göre operator /(double, int)kesinlikle kabul edilebilir. Ardından, dönüşümün başka herhangi bir işlemden önce yapıldığını söylüyor, ancak GCC T2tam sayı türünde olup olmadığını hızlı bir kontrolde sıkıştırabilir b == 0ve öyleyse bir uyarı verebilir . Bunun tamamen standart uyumlu olup olmadığından emin değilim, ancak derleyiciler uyarıları ve ne zaman tetiklenmeleri gerektiğini tanımlama konusunda tam özgürlüğe sahiptir.
Yksisarvinen

2
Burada yerleşik operatörden bahsediyoruz. Bu çok komik. Aslında bir işlev değildir, bu yüzden adresini alamazsınız. Bu nedenle operator/(double,int)gerçekten var olup olmadığını belirleyemezsiniz . Derleyici, örneğin , ile değiştirerek a/bsabiti optimize etmeye karar verebilir . Elbette, bu artık çalışma zamanında değil, daha hızlı arama yaptığınız anlamına gelir . Ama şimdi, onu beslemek zorunda kalacağı sabit, ba * (1/b)operator/(double,double)operator*(double,double)1/0operator*
takılıp

@MSalters Genellikle kayan nokta bölme, muhtemelen 2 gibi istisnai durumlar dışında çarpma ile değiştirilemez.
user202729

2
@ user202729: GCC tamsayı bölme için bile yapar . Şunun bir anlığına batmasına izin verin. GCC, tamsayı bölmesini tam sayı çarpımıyla değiştirir. Evet, bu mümkün, çünkü GCC bir halkada çalıştığını biliyor (sayılar modulo 2 ^ N)
MSalters

9

Bu cevapta UB / UB değil fiyaskosuna girmeyeceğim.

Sadece bunu belirtmek istiyorum 0ve doğru olarak değerlendirilmesine rağmen 0.0 farklılar0 == 0.0 . 0Bir olan intgerçek ve 0.0a, doubledeğişmez.

Bununla birlikte, bu durumda, sonuç aynıdır: d/0kayan nokta bölmesidir, çünkü dçifttir ve dolayısıyla 0dolaylı olarak ikiye dönüştürülür.


5
Bu olağan aritmetik dönüşümler bir bölme belirtmek göz önüne alındığında, ilgili nasıl görmüyorum doublebir tarafından intaraçlarının intdönüştürülür double ve bu standardında belirtildiğinden 0için dönüştürür 0.0 (conv.fpint / 2)
AA

@MM OP 0ile aynı olup olmadığını bilmek istiyor0.0
bolov

2
Soru "Var mı 0 != 0.0?" Diyor . OP asla "aynı" olup olmadıklarını sormaz. Ayrıca bana öyle geliyor ki, sorunun amacı d/0farklı davranıp davranmayacağına bağlıd/0.0
MM

2
@MM - OP sordu . Bu sabit düzenlemelerle gerçekten iyi bir SO özeti göstermiyorlar.
StoryTeller - Unslander Monica

7

Bunu iddia ediyorum foo/0ve foo/0.0aynı değil . Yani, birincinin (tamsayı bölme veya kayan nokta bölme) sonuçta ortaya çıkan etkisi büyük ölçüde türüne bağlıdır foo, ancak aynı şey ikincisi için doğru değildir (her zaman bir kayan nokta bölmesi olacaktır).

İkisinden herhangi birinin UB olup olmadığı önemli değildir. Standarttan alıntı yapmak:

İzin verilebilir tanımsız davranış, durumu tamamen öngörülemeyen sonuçlarla göz ardı etmekten, çevrenin özelliği olan belgelenmiş bir şekilde çeviri veya program yürütme (bir tanılama mesajı yayınlayarak veya yayınlamadan) , bir çeviriyi veya yürütmeyi sonlandırmaya (yayınlama ile) bir tanı mesajı).

(Vurgu benim)

" Doğruluk değeri olarak kullanılan atamayla ilgili parantez öner " uyarısını düşünün : Derleyiciye bir atamanın sonucunu gerçekten kullanmak istediğinizi söylemenin yolu açık olmak ve atamanın etrafına parantez eklemektir. Ortaya çıkan ifade aynı etkiye sahiptir, ancak derleyiciye ne yaptığınızı bildiğinizi söyler. Aynısı şu konularda da söylenebilir foo/0.0: Derleyiciye "Bu kayan nokta bölmesidir" 0.0yerine kullanarak açıkça söylediğiniz için 0, derleyici size güvenir ve bir uyarı vermez.


1
Her ikisinin de onları ortak bir türe dönüştürmek için Olağan aritmetik dönüşümlerden geçmesi gerekir, bu da her iki durumda da kayan nokta bölümü bırakacaktır.
Shafik Yaghmour

@ShafikYaghmour Cevaptaki noktayı kaçırdınız. Türünün ne olduğundan hiç bahsetmediğimi unutmayın foo. Bu kasıtlıdır. İddianız yalnızca fookayan nokta türü olması durumunda doğrudur .
Cássio Renan

Yapmadım, derleyicinin tür bilgisi var ve dönüşümleri anlıyor, belki de metin tabanlı bir statik analizci bu tür şeyler tarafından yakalanabilir, ancak derleyici yapmamalıdır.
Shafik Yaghmour

Demek istediğim, evet, derleyici normal aritmetik dönüşümleri biliyor, ancak programcı açık olduğunda bir uyarı vermemeyi seçiyor. Bütün mesele şu ki, bu muhtemelen bir hata değil, bunun yerine kasıtlı bir davranış.
Cássio Renan

O halde işaret ettiğim dokümantasyon yanlıştır, çünkü her iki durum da kayan nokta bölümüdür. Yani dokümantasyon yanlış veya tanılamada bir hata var.
Shafik Yaghmour

4

Bir gcc böcek gibi bu görünüm, belgelerine -Wno-div-by-zero açıkça söylüyor :

Derleme zamanı tamsayısının sıfıra bölünmesi konusunda uyarmayın. Kayan noktayı sıfıra bölme, sonsuzlukları ve NaN'leri elde etmenin meşru bir yolu olabileceğinden , hakkında uyarılmaz .

ve [ifade.arith.conv] kapsamındaki Olağan aritmetik dönüşümlerden sonra her iki işlenen de çift olacaktır :

Aritmetik veya numaralandırma türünde işlenenler bekleyen birçok ikili işleç, dönüşümlere neden olur ve sonuç türlerini benzer şekilde verir. Amaç, aynı zamanda sonucun türü olan ortak bir tür elde etmektir. Bu model, aşağıdaki gibi tanımlanan normal aritmetik dönüşümler olarak adlandırılır:

...

- Aksi takdirde, işlenenlerden biri double ise diğeri double'a dönüştürülür.

ve [expr.mul] :

* Ve / işlenenleri aritmetik veya kapsamsız numaralandırma türüne sahip olacaktır; % işlenenleri integral veya kapsamsız numaralandırma tipine sahip olacaktır. Genel aritmetik dönüşümler işlenenler üzerinde gerçekleştirilir ve sonucun türünü belirler.

Kayan noktanın sıfıra bölünmesinin tanımsız bir davranış olup olmadığı ve bununla farklı uygulamanın nasıl ele alındığı ile ilgili olarak cevabım burada görünüyor . TL; DR; Görünüşe göre gcc , kayan noktayı sıfıra bölmek için Ek F'ye uyuyor , dolayısıyla tanımsız burada bir rol oynamıyor. Cevap clang için farklı olacaktır.


2

Kayan noktayı sıfıra bölme, tamsayı sıfıra bölmeden farklı davranır.

IEEE kayan nokta + inf ve -inf arasındaki standart diferansiyatları, tamsayılar sonsuz kaydedemezsiniz iken. Tamsayı sıfır sonucuna bölme tanımsız bir davranıştır. Kayan noktayı sıfıra bölme, kayan nokta standardı tarafından tanımlanır ve + inf veya -inf ile sonuçlanır.


2
Bu doğrudur, ancak her iki durumda da kayan nokta bölme işlemi gerçekleştirildiğinden , bunun soruyla nasıl ilgili olduğu açık değildir . OP'nin kodunda tamsayı bölümü yoktur.
Konrad Rudolph
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.