(-2147483648> 0) mengembalikan true dalam C ++?

241

-2147483648 adalah bilangan bulat terkecil untuk tipe bilangan bulat dengan 32 bit, tetapi tampaknya akan meluap dalam if(...)kalimat:

if (-2147483648 > 0)
    std::cout << "true";
else
    std::cout << "false";

Ini akan dicetak truedalam pengujian saya. Namun, jika kita menggunakan -2147483648 ke integer, hasilnya akan berbeda:

if (int(-2147483648) > 0)
    std::cout << "true";
else
    std::cout << "false";

Ini akan dicetak false.

Saya bingung. Adakah yang bisa memberikan penjelasan tentang ini?


Pembaruan 02-05-2012:

Terima kasih atas komentar Anda, di kompiler saya, ukuran int adalah 4 byte. Saya menggunakan VC untuk beberapa pengujian sederhana. Saya telah mengubah uraian dalam pertanyaan saya.

Itu banyak balasan yang sangat baik dalam posting ini, AndreyT memberikan penjelasan yang sangat rinci tentang bagaimana perilaku kompiler pada input tersebut, dan bagaimana integer minimum ini diimplementasikan. qPCR4vir di sisi lain memberikan beberapa "keingintahuan" terkait dan bagaimana bilangan bulat diwakili. Sangat mengesankan!

benil
sumber
48
"kita semua tahu bahwa -2147483648 adalah bilangan bulat terkecil" Itu tergantung pada ukuran bilangan bulat.
orlp
14
"kita semua tahu bahwa -2147483648 adalah bilangan bulat terkecil" - Saya pikir tidak ada bilangan bulat terkecil, karena jumlahnya sangat banyak ... Terserah.
@Inisheer Dengan 4 Byte bilangan bulat Anda mungkin memiliki INT_MINdari -9223372036854775808, jika CHAR_BITadalah 16. Dan bahkan dengan CHAR_BIT == 8dan sizeof(int== 4) `Anda mungkin mendapatkan -9223372036854775807karena C tidak memerlukan 2-Complement angka.
12431234123412341234123

Jawaban:

391

-2147483648bukan "angka". Bahasa C ++ tidak mendukung nilai literal negatif.

-2147483648sebenarnya adalah ekspresi: nilai literal positif 2147483648dengan -operator unary di depannya. Nilai 2147483648tampaknya terlalu besar untuk sisi positif intjangkauan di platform Anda. Jika tipe long intmemiliki jangkauan yang lebih besar pada platform Anda, kompiler harus secara otomatis menganggap bahwa 2147483648ada long inttipe. (Dalam C ++ 11 kompiler juga harus mempertimbangkan long long intjenis.) Ini akan membuat kompiler untuk mengevaluasi -2147483648dalam domain tipe yang lebih besar dan hasilnya akan negatif, seperti yang diharapkan.

Namun, ternyata dalam kasus Anda rentangnya long intsama dengan rentang int, dan secara umum tidak ada tipe integer dengan rentang lebih besar dari intpada platform Anda. Ini secara formal berarti bahwa konstanta positif 2147483648meluap semua jenis integer yang ditandatangani yang tersedia, yang pada gilirannya berarti bahwa perilaku program Anda tidak terdefinisi. (Agak aneh bahwa spesifikasi bahasa memilih untuk perilaku yang tidak terdefinisi dalam kasus-kasus seperti itu, alih-alih membutuhkan pesan diagnostik, tetapi begitulah adanya.)

Dalam praktiknya, dengan mempertimbangkan bahwa perilaku tersebut tidak terdefinisi, 2147483648dapat ditafsirkan sebagai beberapa nilai negatif yang bergantung pada implementasi yang berubah menjadi positif setelah -diterapkan secara unary . Sebagai alternatif, beberapa implementasi mungkin memutuskan untuk mencoba menggunakan tipe yang tidak ditandatangani untuk mewakili nilai (misalnya, dalam kompiler C89 / 90 diharuskan untuk digunakan unsigned long int, tetapi tidak dalam C99 atau C ++). Implementasi diperbolehkan untuk melakukan apa saja, karena perilaku itu tidak ditentukan.

Sebagai catatan, inilah alasan mengapa konstanta seperti INT_MINbiasanya didefinisikan sebagai

#define INT_MIN (-2147483647 - 1)

bukannya yang tampaknya lebih mudah

#define INT_MIN -2147483648

Yang terakhir tidak akan berfungsi sebagaimana dimaksud.

Semut
sumber
78
Ini juga mengapa hal ini dilakukan: #define INT_MIN (-2147483647 - 1).
orlp
5
@ RichardJ.RossIII - dengan dentang Anda mungkin mendapatkan literal 64-bit, karena terlalu besar untuk dimasukkan ke dalam int. Implementasi OP mungkin tidak memiliki tipe 64-bit.
Carl Norum
1
@ RichardJ.RossIII: Saya percaya perilaku ini adalah implementasi-didefinisikan / tidak terdefinisi.
Oliver Charlesworth
3
Saya tidak pernah berpikir bahwa "angka negatif" tidak diuraikan seperti itu. Saya tidak melihat alasannya. Saya harap itu -1.0diuraikan sebagai nilai ganda negatif, bukan?
Tema
6
@ qPCR4vir: Tidak. Seperti yang saya tulis dalam komentar saya atas jawaban Anda, baik C atau C ++ modern tidak mengizinkan penggunaan tipe yang tidak ditandai dalam kasus ini (dengan konstanta desimal yang tidak di-unix ). Hanya standar C pertama (C89 / 90) yang diizinkan unsigned long intdalam konteks ini, tetapi dalam C99 izin ini dihapus. Literal yang tidak terhubung dalam C dan C ++ harus memiliki tipe yang sudah ditandatangani . Jika Anda melihat tipe yang tidak ditandatangani di sini ketika yang ditandatangani akan berfungsi, itu berarti kompiler Anda rusak. Jika Anda melihat tipe yang tidak ditandai di sini saat tidak ada tipe yang ditandatangani, maka ini hanyalah manifestasi spesifik dari perilaku yang tidak terdefinisi.
AnT
43

Compiler (VC2012) mempromosikan ke integer "minimum" yang dapat menyimpan nilai-nilai. Dalam kasus pertama, signed int(dan long int) tidak bisa (sebelum tanda diterapkan), tetapi unsigned intdapat: 2147483648memilikiunsigned int ???? Tipe. Di detik Anda memaksa intdari unsigned.

const bool i= (-2147483648 > 0) ;  //   --> true

peringatan C4146: operator minus unary diterapkan pada tipe yang tidak ditandatangani , hasilnya masih belum ditandatangani

Berikut ini "keingintahuan" terkait:

const bool b= (-2147483647      > 0) ; //  false
const bool i= (-2147483648      > 0) ; //  true : result still unsigned
const bool c= ( INT_MIN-1       > 0) ; //  true :'-' int constant overflow
const bool f= ( 2147483647      > 0) ; //  true
const bool g= ( 2147483648      > 0) ; //  true
const bool d= ( INT_MAX+1       > 0) ; //  false:'+' int constant overflow
const bool j= ( int(-2147483648)> 0) ; //  false : 
const bool h= ( int(2147483648) > 0) ; //  false
const bool m= (-2147483648L     > 0) ; //  true 
const bool o= (-2147483648LL    > 0) ; //  false

Standar C ++ 11 :

2.14.2 Literal integer [lex.icon]

...

Bilangan bulat integer adalah urutan digit yang tidak memiliki periode atau bagian eksponen. Literal integer mungkin memiliki awalan yang menentukan basisnya dan akhiran yang menentukan jenisnya.

...

Jenis literal integer adalah yang pertama dari daftar yang sesuai di mana nilainya dapat diwakili.

masukkan deskripsi gambar di sini

Jika bilangan bulat integer tidak dapat diwakili oleh tipe apa pun dalam daftar dan tipe integer yang diperluas (3.9.1) dapat mewakili nilainya, itu mungkin memiliki tipe integer yang diperluas. Jika semua tipe dalam daftar untuk literal ditandatangani, tipe integer yang diperluas harus ditandatangani. Jika semua tipe dalam daftar untuk literal tidak ditandai, tipe integer yang diperluas harus tidak ditandatangani. Jika daftar berisi tipe yang ditandatangani dan tidak ditandatangani, tipe integer yang diperluas dapat ditandatangani atau tidak ditandatangani. Suatu program tidak terbentuk dengan baik jika salah satu unit terjemahannya mengandung bilangan bulat integer yang tidak dapat diwakili oleh jenis yang diizinkan.

Dan ini adalah aturan promosi untuk bilangan bulat dalam standar.

4.5 Promosi integral [conv.prom]

Sebuah prvalue dari tipe integer selain bool, char16_t, char32_t, atau wchar_tyang bulat konversi rank (4.13) kurang dari pangkat int dapat dikonversi ke prvalue jenis intjika intdapat mewakili semua nilai-nilai dari jenis sumber; jika tidak, prvalue sumber dapat dikonversi ke prvalue tipe unsigned int.

qPCR4vir
sumber
3
@ qPCR4vir: Dalam C89 / 90 para penyusun seharusnya jenis penggunaan int, long int, unsigned long intuntuk mewakili konstanta desimal unsuffixed. Itu adalah satu-satunya bahasa yang memungkinkan menggunakan tipe yang tidak ditandatangani untuk konstanta desimal yang tidak tetap. Di C ++ 98 itu intatau long int. Tidak ada tipe yang tidak ditandatangani diizinkan. Baik C (mulai dari C99) maupun C ++ tidak mengizinkan kompiler untuk menggunakan tipe yang tidak ditandatangani dalam konteks ini. Kompiler Anda, tentu saja, bebas untuk menggunakan tipe yang tidak ditandatangani jika tidak ada yang ditandatangani bekerja, tetapi ini masih hanya manifestasi spesifik dari perilaku yang tidak terdefinisi.
AnT
@AndreyT. Bagus! Tentu saja, kekuatanmu. Apakah VC2012 rusak?
qPCR4vir
@ qPCR4vir: AFAIK, VC2012 belum merupakan kompiler C ++ 11 (apakah itu?), yang artinya harus menggunakan salah satu intatau long intuntuk mewakili 2147483648. Juga, AFAIK, di VC2012 keduanya intdan long inttipe 32-bit. Ini berarti bahwa dalam VC2012 literal 2147483648harus mengarah pada perilaku yang tidak terdefinisi . Ketika perilaku tidak terdefinisi, kompiler diizinkan untuk melakukan apa saja. Itu berarti VC2012 tidak rusak. Itu hanya mengeluarkan pesan diagnostik yang menyesatkan. Alih-alih memberi tahu Anda bahwa perilaku tidak jelas, ia memutuskan untuk menggunakan tipe yang tidak ditandatangani.
AnT
@AndreyT: Apakah Anda mengatakan bahwa kompiler bebas untuk mengeluarkan setan hidung jika kode sumber berisi literal desimal yang tidak dikunci yang melebihi nilai maksimum dari yang ditandatangani long, dan tidak diharuskan mengeluarkan diagnostik? Itu akan tampak rusak.
supercat
"Peringatan C4146" yang sama di VS2008 dan "konstanta desimal ini tidak ditandatangani hanya di ISO C90" di G ++
spyder
6

Dalam pendek, 2147483648berlimpah-limpah -2147483648, dan (-(-2147483648) > 0)adalah true.

Ini adalah bagaimana 2147483648penampilan seperti dalam biner.

Selain itu, dalam kasus perhitungan biner yang ditandatangani, bit yang paling signifikan ("MSB") adalah bit tanda. Pertanyaan ini dapat membantu menjelaskan alasannya.

drzymala
sumber
4

Karena -2147483648sebenarnya 2147483648dengan negasi ( -) yang diterapkan padanya, angkanya tidak seperti yang Anda harapkan. Ini sebenarnya sama dengan pseudocode ini:operator -(2147483648)

Sekarang, anggap kompiler Anda sizeof(int)sama dengan 4dan CHAR_BITdidefinisikan sebagai 8, yang akan membuat 2147483648melimpahi nilai maksimum yang ditandatangani integer ( 2147483647). Jadi, apa plus maksimum satu? Mari kita selesaikan dengan integer pujian 2s, 2s.

Tunggu! 8 melebihi integer! Apa yang kita lakukan? Gunakan representasi unsigned dari 1000dan menafsirkan bit sebagai integer yang ditandatangani. Representasi ini membuat kita -8menerapkan negasi komplemen 2s yang menghasilkan 8, yang, seperti kita ketahui, lebih besar dari 0.

Inilah sebabnya <limits.h>(dan <climits>) biasanya didefinisikan INT_MINsebagai ((-2147483647) - 1)- sehingga bilangan bulat bertanda maksimum ( 0x7FFFFFFF) dinegasikan ( 0x80000001), lalu dikurangi ( 0x80000000).

Cole Johnson
sumber
Untuk angka 4 bit, negasi komplemen keduanya -8masih -8.
Ben Voigt
Kecuali bahwa -8 diartikan sebagai 0-8, bukan negatif 8. Dan 8 meluap 4 bit int
Cole Johnson
Pertimbangkan -(8)yang dalam C ++ sama dengan -8- itu negasi diterapkan pada literal, bukan literal negatif. Secara literal adalah 8, yang tidak sesuai dengan bilangan bulat 4-bit yang ditandatangani, jadi harus tidak ditandatangani. Polanya adalah 1000. Sejauh ini jawaban Anda benar. Negasi komplemen keduanya 1000dalam 4 bit adalah 1000, tidak masalah apakah itu ditandatangani atau tidak. Jawaban Anda, mengatakan "menafsirkan bit sebagai bilangan bulat yang ditandatangani" yang membuat nilai -8setelah negasi komplemen keduanya, sama seperti sebelum negasi.
Ben Voigt
Tentu saja, dalam "4-bit C ++" tidak ada "menafsirkan bit sebagai langkah integer yang ditandatangani". Literal menjadi tipe terkecil yang dapat mengekspresikannya, yang merupakan bilangan bulat 4-bit yang tidak ditandatangani . Nilai literalnya adalah 8. Negasi diterapkan (modulo 16), menghasilkan jawaban akhir 8. Pengkodean masih 1000 tetapi nilainya berbeda karena jenis yang tidak ditandatangani dipilih.
Ben Voigt