Przypadek 1:
#include <iostream>
int main()
{
double d = 15.50;
std::cout<<(d/0.0)<<std::endl;
}
Kompiluje się bez żadnych ostrzeżeń i wydruków inf
. OK, C ++ radzi sobie z dzieleniem przez zero ( zobacz na żywo ).
Ale,
Przypadek 2:
#include <iostream>
int main()
{
double d = 15.50;
std::cout<<(d/0)<<std::endl;
}
Kompilator daje następujące ostrzeżenie ( zobacz na żywo ):
warning: division by zero [-Wdiv-by-zero]
std::cout<<(d/0)<<std::endl;
Dlaczego kompilator daje ostrzeżenie w drugim przypadku?
Jest 0 != 0.0
?
Edytować:
#include <iostream>
int main()
{
if(0 == 0.0)
std::cout<<"Same"<<std::endl;
else
std::cout<<"Not same"<<std::endl;
}
wynik:
Same
c++
gcc
floating-point
divide-by-zero
Jayesh
źródło
źródło
Odpowiedzi:
Dzielenie zmiennoprzecinkowe przez zero jest dobrze zdefiniowane przez IEEE i daje nieskończoność (dodatnią lub ujemną w zależności od wartości licznika (lub
NaN
dla ± 0) ).W przypadku liczb całkowitych nie ma sposobu, aby przedstawić nieskończoność, a język definiuje operację tak, aby miała niezdefiniowane zachowanie, więc kompilator z łatwością próbuje odciągnąć Cię od tej ścieżki.
Jednak w tym przypadku, ponieważ licznikiem jest a
double
, dzielnik (0
) również powinien zostać podwyższony do podwójnej wartości i nie ma powodu, aby dawać ostrzeżenie, nie dając ostrzeżenia,0.0
więc myślę, że jest to błąd kompilatora.źródło
d/0
,0
jest konwertowane na typd
.W standardowym C ++ oba przypadki są niezdefiniowanym zachowaniem . Wszystko może się zdarzyć, łącznie z formatowaniem dysku twardego. Nie należy oczekiwać ani polegać na „return inf. Ok” ani na jakimkolwiek innym zachowaniu.
Kompilator najwyraźniej decyduje się na ostrzeżenie w jednym przypadku, a nie w drugim, ale to nie znaczy, że jeden kod jest w porządku, a drugi nie. To tylko dziwactwo generowanych przez kompilator ostrzeżeń.
Ze standardu C ++ 17 [wyr.mul] / 4:
źródło
std::numeric_limits<T>::is_iec559
taktrue
, to dzielenie przez zeroT
nie jest UB (i na większości platform jesttrue
dladouble
ifloat
, chociaż być przenośnym, trzeba to wyraźnie zaznaczyć za pomocąif
lubif constexpr
).is_iec559
jakotrue
oznacza, że implementacja dokumentuje zachowanie, które norma nie definiuje . Po prostu jest to jeden przypadek, w którym dokumentację implementacji można odczytać programowo. Nawet nie jedyny: to samo dotyczyis_modulo
liczb całkowitych ze znakiem.Moim najlepszym przypuszczeniem, aby odpowiedzieć na to konkretne pytanie, byłoby to, że kompilator emituje ostrzeżenie przed wykonaniem konwersji
int
dodouble
.Tak więc kroki wyglądałyby tak:
/(T, T2)
, w którymT=double
,T2=int
.std::is_integral<T2>::value
jesttrue
ib == 0
- to wyzwala ostrzeżenie.T2
nadouble
Jest to oczywiście spekulacja i opiera się na specyfikacjach zdefiniowanych przez kompilator. Ze standardowego punktu widzenia mamy do czynienia z możliwymi niezdefiniowanymi zachowaniami.
Należy pamiętać, że jest to oczekiwane zachowanie zgodnie z dokumentacją GCC
(przy okazji wydaje się, że ta flaga nie może być używana jawnie w GCC 8.1)
źródło
/
aby wiedzieć, że jest dzielony. Gdyby lewa strona byłaFoo
przedmiotem i byłbyoperator/(Foo, int)
, to może nawet nie być podziałem. Kompilator zna podział tylko wtedy, gdy wybrałbuilt-in / (double, double)
niejawną konwersję prawej strony. Ale to znaczy, że NIE dzieli przezint(0)
, tylko dzielidouble(0)
.operator /(double, int)
jest z pewnością akceptowalna. Następnie mówi, że konwersja jest wykonywana przed jakąkolwiek inną akcją, ale GCC może wcisnąć w szybkie sprawdzenie, czyT2
jest to typ całkowityb == 0
i wyemitować ostrzeżenie, jeśli tak. Nie jestem pewien, czy jest to w pełni zgodne ze standardami, ale kompilatory mają pełną swobodę w definiowaniu ostrzeżeń i kiedy powinny być uruchamiane.operator/(double,int)
naprawdę istnieje. Kompilator może na przykład zdecydować się na optymalizacjęa/b
pod kątem stałejb
, zastępując jąa * (1/b)
. Oczywiście oznacza to, że nie dzwonisz jużoperator/(double,double)
w czasie wykonywania, ale szybciejoperator*(double,double)
. Ale teraz to optymalizator się1/0
operator*
W tej odpowiedzi nie będę wchodził w porażkę UB / nie UB.
Chcę tylko na to zwrócić uwagę
0
i jestem0.0
inny, mimo że0 == 0.0
oceniam jako prawda.0
jestint
literałem i0.0
jestdouble
literałem.Jednak w tym przypadku wynik końcowy jest taki sam:
d/0
jest dzieleniem zmiennoprzecinkowym, ponieważd
jest podwójny, a więc0
jest niejawnie konwertowany na podwójny.źródło
double
przezint
środek, na któryint
jest konwertowanedouble
, i jest określone w standardzie, który0
konwertuje na0.0
(konw. Fpint / 2)0
to to samo co0.0
0 != 0.0
?”. OP nigdy nie pyta, czy są „takie same”. Wydaje mi się również, że celem pytania jest to, czyd/0
można zachowywać się inaczej niżd/0.0
Uważam, że
foo/0
ifoo/0.0
to nie to samo. Mianowicie wynikowy efekt pierwszego (dzielenie całkowite lub zmiennoprzecinkowe) jest silnie zależny od typufoo
, podczas gdy to samo nie dotyczy drugiego (zawsze będzie to dzielenie zmiennoprzecinkowe).Nie ma znaczenia, czy którykolwiek z nich jest UB. Cytując standard:
(Podkreślenie moje)
Rozważ ostrzeżenie „ sugeruj nawiasy wokół przypisania używanego jako wartość prawdy ”: Sposobem na poinformowanie kompilatora, że naprawdę chcesz użyć wyniku przypisania, jest wyraźne umieszczenie przypisania i dodanie nawiasów wokół przypisania. Wynikowa instrukcja ma ten sam efekt, ale informuje kompilator, że wiesz, co robisz. To samo można powiedzieć o
foo/0.0
: skoro wyraźnie mówisz kompilatorowi „To jest dzielenie zmiennoprzecinkowe” używając0.0
zamiast0
, kompilator ufa ci i nie wyśle ostrzeżenia.źródło
foo
. To jest zamierzone. Twoje stwierdzenie jest prawdziwe tylko w przypadkufoo
typu zmiennoprzecinkowego.Wygląda to na błąd w gcc, dokumentacja
-Wno-div-by-zero
jasno mówi :a po zwykłych konwersjach arytmetycznych uwzględnionych w [wyraż.arith.conv] oba operandy będą podwójne :
i [wyr.mul] :
W odniesieniu do tego, czy dzielenie zmiennoprzecinkowe przez zero jest niezdefiniowanym zachowaniem i jak radzą sobie z tym różne implementacje, wydaje mi się, że tutaj odpowiedź . TL; DR; Wygląda na to, że gcc jest zgodne z załącznikiem F wrt do dzielenia zmiennoprzecinkowego przez zero, więc wartość undefined nie odgrywa tutaj roli. W przypadku brzęku odpowiedź byłaby inna.
źródło
Dzielenie zmiennoprzecinkowe przez zero zachowuje się inaczej niż dzielenie liczby całkowitej przez zero.
W IEEE zmiennoprzecinkowych standardowe rozróżnia + inf i -Inf, natomiast całkowite nie można przechowywać w nieskończoność. Wynik dzielenia liczby całkowitej przez zero jest niezdefiniowanym zachowaniem. Dzielenie zmiennoprzecinkowe przez zero jest zdefiniowane przez standard zmiennoprzecinkowy i daje w wyniku + inf lub -inf.
źródło