Kasus 1:
#include <iostream>
int main()
{
double d = 15.50;
std::cout<<(d/0.0)<<std::endl;
}
Ini mengkompilasi tanpa peringatan dan cetakan apa pun inf
. OK, C ++ dapat menangani pembagian dengan nol, ( lihat langsung ).
Tapi,
Kasus 2:
#include <iostream>
int main()
{
double d = 15.50;
std::cout<<(d/0)<<std::endl;
}
Kompilator memberikan peringatan berikut ( lihat langsung ):
warning: division by zero [-Wdiv-by-zero]
std::cout<<(d/0)<<std::endl;
Mengapa kompilator memberikan peringatan pada kasus kedua?
Apakah 0 != 0.0
?
Edit:
#include <iostream>
int main()
{
if(0 == 0.0)
std::cout<<"Same"<<std::endl;
else
std::cout<<"Not same"<<std::endl;
}
keluaran:
Same
c++
gcc
floating-point
divide-by-zero
Jayesh
sumber
sumber
Jawaban:
Pembagian floating point dengan nol didefinisikan dengan baik oleh IEEE dan memberikan tak terbatas (baik positif atau negatif sesuai dengan nilai pembilang (atau
NaN
untuk ± 0) ).Untuk integer, tidak ada cara untuk merepresentasikan infinity dan bahasanya mendefinisikan operasi untuk memiliki perilaku yang tidak terdefinisi sehingga compiler dengan senang hati mencoba untuk mengarahkan Anda keluar dari path tersebut.
Namun dalam kasus ini, karena pembilangnya adalah a
double
, divisor (0
) harus dipromosikan menjadi double juga dan tidak ada alasan untuk memberikan peringatan di sini sementara tidak memberikan peringatan,0.0
jadi saya pikir ini adalah bug compiler.sumber
d/0
,0
diubah menjadi jenisd
.Dalam Standar C ++, kedua kasus adalah perilaku yang tidak ditentukan . Apa pun dapat terjadi, termasuk memformat hard drive Anda. Anda tidak boleh mengharapkan atau mengandalkan "return inf. Ok" atau perilaku lainnya.
Kompiler tampaknya memutuskan untuk memberikan peringatan dalam satu kasus dan bukan yang lain, tetapi ini tidak berarti bahwa satu kode OK dan yang satu tidak. Ini hanyalah kekhasan dari generasi peringatan yang dibuat oleh kompiler.
Dari standar C ++ 17 [expr.mul] / 4:
sumber
std::numeric_limits<T>::is_iec559
initrue
, maka pembagian dengan nol untukT
tidak UB (dan pada kebanyakan platform itutrue
untukdouble
danfloat
, meskipun menjadi portabel Anda akan perlu secara eksplisit memeriksanya denganif
atauif constexpr
).is_iec559
sebagaitrue
sarana bahwa implementasi mendokumentasikan perilaku yang tidak ditentukan oleh standar. Hanya saja ini adalah salah satu kasus di mana dokumentasi implementasinya dapat dibaca secara terprogram. Bahkan bukan satu-satunya: hal yang sama berlakuis_modulo
untuk jenis bilangan bulat bertanda.Tebakan terbaik saya untuk menjawab pertanyaan khusus ini adalah bahwa kompiler mengeluarkan peringatan sebelum melakukan konversi
int
kedouble
.Jadi, langkah-langkahnya seperti ini:
/(T, T2)
, di manaT=double
,T2=int
.std::is_integral<T2>::value
adalahtrue
danb == 0
- pemicu peringatan ini.T2
kedouble
Ini tentu saja spekulasi dan didasarkan pada spesifikasi yang ditentukan kompiler. Dari sudut pandang standar, kita berurusan dengan kemungkinan Perilaku Tidak Terdefinisi.
Perhatikan bahwa ini adalah perilaku yang diharapkan menurut dokumentasi GCC
(btw. Tampaknya tanda ini tidak dapat digunakan secara eksplisit di GCC 8.1)
sumber
/
untuk mengetahui bahwa pembagiannya. Jika sisi kiri akan menjadi sebuahFoo
objek, dan akan ada aoperator/(Foo, int)
, maka itu mungkin bukan pembagian. Kompilator hanya mengetahui pembagiannya ketika ia telah memilihbuilt-in / (double, double)
menggunakan konversi implisit dari sisi kanan. Tapi itu berarti BUKAN melakukan pembagian denganint(0)
, itu melakukan pembagian dengandouble(0)
.operator /(double, int)
tentu bisa diterima. Kemudian, dikatakan bahwa konversi dilakukan sebelum tindakan lain, tetapi GCC dapat memeras pemeriksaan cepat jikaT2
berjenis integer danb == 0
dan mengeluarkan peringatan jika demikian. Tidak yakin apakah itu sepenuhnya sesuai standar, tetapi penyusun memiliki kebebasan penuh dalam menentukan peringatan dan kapan mereka harus dipicu.operator/(double,int)
benar-benar ada. Kompilator dapat misalnya memutuskan untuk mengoptimalkana/b
konstantab
dengan menggantinya dengana * (1/b)
. Tentu saja, itu berarti Anda tidak lagi meneleponoperator/(double,double)
pada saat runtime tetapi semakin cepatoperator*(double,double)
. Tapi sekarang pengoptimal yang1/0
operator*
Saya tidak akan masuk ke UB / bukan UB bencana di jawaban ini.
Saya hanya ingin menunjukkan itu
0
dan0.0
berbeda meskipun0 == 0.0
menilai benar.0
adalahint
literal dan0.0
merupakandouble
literal.Namun dalam kasus ini, hasil akhirnya sama:
d/0
adalah pembagian floating point karenad
ganda dan0
secara implisit diubah menjadi double.sumber
double
denganint
cara yangint
dikonversidouble
, dan ditentukan dalam standar yang0
dikonversi menjadi0.0
(konv.fpint / 2)0
sama dengan0.0
0 != 0.0
?". OP tidak pernah menanyakan apakah mereka "sama". Juga bagi saya tampaknya maksud dari pertanyaan tersebut adalah untuk dilakukan dengan apakahd/0
dapat berperilaku berbedad/0.0
Saya berpendapat bahwa
foo/0
danfoo/0.0
yang tidak sama. Yakni, efek yang dihasilkan dari yang pertama (pembagian integer atau pembagian floating point) sangat tergantung pada jenisnyafoo
, sedangkan hal yang sama tidak berlaku untuk yang kedua (itu akan selalu menjadi pembagian floating point).Apakah salah satu dari keduanya adalah UB tidak relevan. Mengutip standar:
(Penekanan saya)
Pertimbangkan peringatan " sarankan tanda kurung di sekitar tugas yang digunakan sebagai nilai kebenaran ": Cara untuk memberi tahu compiler bahwa Anda benar - benar ingin menggunakan hasil tugas adalah dengan menjadi eksplisit, dan menambahkan tanda kurung di sekitar tugas. Pernyataan yang dihasilkan memiliki efek yang sama, tetapi memberi tahu kompiler bahwa Anda tahu apa yang Anda lakukan. Hal yang sama dapat dikatakan tentang
foo/0.0
: Karena Anda secara eksplisit memberi tahu compiler "Ini adalah pembagian floating point" dengan menggunakan0.0
alih-alih0
, kompilator mempercayai Anda dan tidak akan mengeluarkan peringatan.sumber
foo
. Itu disengaja. Penegasan Anda hanya benar dalam kasus itufoo
adalah tipe titik mengambang.Ini terlihat seperti bug gcc, dokumentasinya dengan
-Wno-div-by-zero
jelas mengatakan :dan setelah konversi aritmatika biasa yang tercakup dalam [expr.arith.conv] kedua operan akan menjadi dua kali lipat :
dan [expr.mul] :
Sehubungan dengan apakah floating point dibagi nol adalah perilaku tidak terdefinisi dan bagaimana implementasi yang berbeda menghadapinya, tampaknya jawaban saya di sini . TL; DR; Sepertinya gcc sesuai dengan Lampiran F wrt ke floating point dibagi nol, jadi tidak ditentukan tidak berperan di sini. Jawabannya akan berbeda untuk dentang.
sumber
Pembagian floating point dengan nol berperilaku berbeda dari pembagian integer dengan nol.
The IEEE floating point membedakan standar antara inf + dan -inf, sementara bilangan bulat tidak bisa menyimpan tak terhingga. Pembagian bilangan bulat dengan hasil nol adalah perilaku yang tidak terdefinisi. Pembagian floating point dengan nol ditentukan oleh standar floating point dan menghasilkan + inf atau -inf.
sumber