Saya pikir pertanyaan ini cukup jelas, saya kira itu mungkin memiliki sesuatu untuk dilakukan dengan meluap tapi aku masih tidak cukup mendapatkannya. Apa yang terjadi, bitwise, di bawah tenda?
Mengapa -(-2147483648) = -2147483648
(setidaknya saat kompilasi dalam C)?
Ungkapan -(-2147483648)
yang sempurna didefinisikan dalam C, namun hal ini mungkin tidak jelas mengapa hal ini dengan cara ini.
Ketika anda menulis -2147483648
, hal ini dibentuk sebagai operator unary minus diterapkan untuk konstanta integer. Jika 2147483648
dapat't dapat dinyatakan sebagai int
, maka s adalah direpresentasikan sebagai panjang
atau lama
* (mana yang cocok untuk pertama), di mana jenis yang terakhir ini dijamin oleh C Standar untuk menutupi bahwa nilai†.
Untuk memastikannya, anda bisa memeriksanya dengan:
printf("%zu\n", sizeof(-2147483648));
yang menghasilkan 8
di mesin saya.
Langkah berikutnya adalah untuk menerapkan kedua -
operator, dalam hal ini nilai akhir adalah 2147483648L
(dengan asumsi bahwa ini adalah akhirnya direpresentasikan sebagai lama
). Jika anda mencoba untuk menetapkan ke int
objek, sebagai berikut:
int n = -(-2147483648);
kemudian perilaku aktual pelaksanaan yang ditetapkan. Mengacu pada Standar:
C11 §6.3.1.3/3 Signed dan unsigned integer
Jika tidak, jenis baru menandatangani dan nilai tidak dapat diwakilkan di dalamnya; baik hasilnya adalah pelaksanaan yang ditetapkan atau pelaksanaan yang ditetapkan sinyal yang dibangkitkan.
Cara yang paling umum adalah untuk hanya cut-off bit yang lebih tinggi. Misalnya, GCC dokumen sebagai:
Untuk konversi ke tipe lebar N, nilai berkurang modulo 2^N untuk berada dalam berbagai jenis; tidak ada sinyal yang dibangkitkan.
Secara konseptual, konversi ke tipe lebar 32 dapat diilustrasikan dengan bitwise DAN operasi:
value & (2^32 - 1) // preserve 32 least significant bits
Sesuai dengan dua's pelengkap aritmatika, nilai n
terbentuk dengan semua nol dan MSB (sign) bit set, yang mewakili nilai dari -2^31, yang
-2147483648`.
int
objek:Jika anda mencoba untuk meniadakan int
objek, yang memegang nilai -2147483648
, maka dengan asumsi dua's melengkapi mesin, program ini akan menunjukkan undefined perilaku:
n = -n; // UB if n == INT_MIN and INT_MAX == 2147483647
C11 §6.5/5 Ekspresi
Jika kondisi yang luar biasa terjadi selama evaluasi yang ekspresi (artinya, jika hasilnya tidak didefinisikan secara matematis atau tidak dalam kisaran nilai-nilai yang dapat direpresentasikan untuk jenisnya), perilaku undefined.
*) Di withdrawed C90 Standar, tidak ada lama
jenis dan aturan yang berbeda. Secara khusus, urutan untuk unsuffixed desimal adalah int
, long int
, unsigned long int
(C90 §6.1.3.2 konstanta Integer).
†) Ini adalah karena LLONG_MAX
, yang harus setidaknya +9223372036854775807
(C11 §5.2.4.2.1/1).
[3]: https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow#INT32-C. Ensurethatoperationsonsignedintegersdonotresultinoverflow-UnaryNegation
Catatan: jawaban ini tidak berlaku seperti itu pada usang ISO C90 standar yang masih banyak digunakan oleh compiler
Pertama-tama, pada C99, C11, ekspresi -(-2147483648) == -2147483648
sebenarnya palsu:
int is_it_true = (-(-2147483648) == -2147483648);
printf("%d\n", is_it_true);
cetakan
0
Jadi bagaimana mungkin bahwa ini mengevaluasi ke true?
Mesin ini menggunakan 32-bit dua's pelengkap bilangan bulat. The 2147483648
adalah sebuah konstanta integer yang cukup doesn't cocok di 32 bit, dengan demikian itu akan menjadi baik int panjang
atau long long int
tergantung pada mana yang dulu mana yang cocok. Ini diabaikan akan mengakibatkan -2147483648
- dan lagi, meskipun jumlah -2147483648
bisa masuk dalam 32-bit integer, ekspresi -2147483648
terdiri dari >32-bit bilangan bulat positif yang diawali dengan unary -
!
Anda dapat mencoba program berikut:
#include <stdio.h>
int main() {
printf("%zu\n", sizeof(2147483647));
printf("%zu\n", sizeof(2147483648));
printf("%zu\n", sizeof(-2147483648));
}
Output pada mesin tersebut kemungkinan besar akan menjadi 4, 8 dan 8.
Sekarang, -2147483648
menegasikan lagi akan mengakibatkan +214783648
, yang masih dari jenis int panjang
atau long long int
, dan semuanya baik-baik saja.
Di C99, C11, integer ekspresi konstan -(-2147483648)
yang didefinisikan pada semua sesuai implementasi.
Sekarang, ketika nilai ini ditugaskan untuk variabel jenis int
, dengan 32 bit dan dua's melengkapi representasi, nilai ini tidak dapat direpresentasikan di dalamnya - nilai pada 32-bit 2's melengkapi akan berkisar dari -2147483648 sampai 2147483647.
Yang C11 standar 6.3.1.3p3 mengatakan sebagai berikut: integer konversi:
- [Ketika] jenis baru menandatangani dan nilai yang tidak dapat diwakili di dalamnya; baik hasilnya adalah pelaksanaan yang ditetapkan atau pelaksanaan yang ditetapkan sinyal yang dibangkitkan.
Artinya, C standar doesn't benar-benar menentukan apa nilai dalam hal ini akan menjadi, atau tidak't menghalangi kemungkinan bahwa eksekusi program berhenti karena sinyal yang dibangkitkan, tapi daun itu untuk implementasi (yaitu compiler) untuk memutuskan bagaimana untuk menangani hal itu (C11 3.4.1):
pelaksanaan-perilaku didefinisikan
tidak ditentukan perilaku di mana masing-masing dokumen pelaksanaan cara yang biasa dilakukan
dan (3.19.1):
pelaksanaan-nilai yang ditetapkan
tidak ditentukan nilai di mana masing-masing dokumen pelaksanaan cara yang biasa dilakukan
Dalam kasus anda, implementasi didefinisikan perilaku adalah bahwa nilai adalah 32 terendah-order bit [*]. Karena 2's pelengkap, (panjang) panjang int nilai 0x80000000
memiliki sedikit 31 set dan semua bit lainnya dibersihkan. Dalam 32-bit dua's melengkapi bilangan bulat bit 31 adalah tanda bit - yang berarti bahwa jumlah negatif; semua nilai bit memusatkan perhatian berarti bahwa nilai minimum representable nomor, yaitu INT_MIN
.
[*] GCC dokumen yang pelaksanaannya ditetapkan perilaku dalam hal ini sebagai berikut:
hasil dari, atau sinyal yang dibangkitkan oleh, mengkonversi integer integer ditandatangani jenis ketika nilai tidak dapat diwakili dalam sebuah objek dari jenis (C90 6.2.1.2, C99 dan C11 6.3.1.3).
Untuk konversi ke tipe lebar
N
, nilai berkurang modulo2^N
ke dalam berbagai jenis; tidak ada sinyal yang dibangkitkan.
Ini bukan C pertanyaan, untuk di implementasi C menampilkan 32-bit dua's melengkapi representasi untuk jenis int
, efek dari penerapan unary operator negasi untuk sebuah int
memiliki nilai -2147483648
adalah terdefinisi. Artinya, bahasa C secara khusus membantah menunjuk hasil evaluasi operasi tersebut.
Mempertimbangkan secara lebih umum, namun, bagaimana unary -
operator didefinisikan dalam dua's melengkapi aritmatika: kebalikan dari angka positif x terbentuk dengan membalik semua bit dari biner representasi, dan menambahkan 1
. Ini sama dengan definisi yang berfungsi baik untuk setiap angka negatif yang memiliki setidaknya satu bit lain dari sign bit set.
Masalah kecil muncul, namun, untuk dua angka-angka yang tidak memiliki nilai bit set: 0, yang tidak memiliki bit set di semua, dan jumlah yang hanya memiliki tanda bit set (-2147483648 di 32-bit representasi). Ketika anda flip semua bit dari salah satu dari ini, anda berakhir dengan semua nilai bit set. Oleh karena itu, ketika anda kemudian tambahkan 1, hasil melimpah nilai bit. Jika anda membayangkan melakukan itu karena jika nomor yang unsigned, mengobati sign bit yang berupa nilai bit, maka anda mendapatkan
-2147483648 (decimal representation)
--> 0x80000000 (convert to hex)
--> 0x7fffffff (flip bits)
--> 0x80000000 (add one)
--> -2147483648 (convert to decimal)
Serupa berlaku untuk pembalik nol, tetapi dalam kasus yang meluap setelah menambahkan 1 meluap mantan sign bit juga. Jika overflow diabaikan, yang dihasilkan 32 low-order bit semua nol, maka -0 == 0.
I'm akan menggunakan 4-bit nomor, hanya untuk membuat matematika sederhana, tetapi gagasan adalah sama.
Dalam 4-bit nomor, nilai yang mungkin adalah antara 0000 dan 1111. Itu akan menjadi 0 sampai 15, namun jika anda ingin mewakili angka negatif, bit pertama digunakan untuk menunjukkan tanda-tanda (0 untuk positif dan 1 untuk negatif).
Jadi 1111 tidak 15. Sebagai bit pertama adalah 1,'s angka negatif. Untuk mengetahui nilainya, kita menggunakan dua melengkapi metode seperti yang sudah dijelaskan di jawaban sebelumnya: "membalikkan bit dan tambahkan 1":
0001 di biner 1 dalam desimal, sehingga 1111 adalah -1.
Dua-melengkapi metode berjalan dua arah, jadi jika anda menggunakannya dengan nomor apapun, itu akan memberi anda representasi biner dari angka tersebut dengan tanda terbalik.
Sekarang mari's melihat 1000. Bit pertama adalah 1, jadi itu's angka negatif. Menggunakan dua melengkapi metode:
Jadi 1000 adalah -8. Jika kita melakukan -(-8)
, dalam biner artinya -(1000)
, yang sebenarnya berarti menggunakan dua melengkapi metode dalam 1000. Seperti yang kita lihat di atas, hasilnya juga 1000.
Jadi, dalam 4-bit nomor, -(-8)
adalah sama dengan -8.
Di nomor 32-bit, -2147483648
dalam biner adalah 1000..(31 nol)
, tetapi jika anda menggunakan dua melengkapi metode, anda'll berakhir dengan nilai yang sama (hasilnya adalah jumlah yang sama).
Yang's mengapa dalam 32-bit nomor -(-2147483648)
adalah sama dengan -2147483648
Hal ini tergantung pada versi dari C, spesifikasi implementasi dan apakah kita berbicara tentang variabel atau nilai-nilai literal.
Hal pertama yang harus dipahami adalah bahwa ada tidak ada negatif integer literals dalam C "-2147483648" adalah unary minus operasi diikuti oleh bilangan bulat positif literal.
Mari kita asumsikan bahwa kita sedang berjalan di sebuah khas 32-bit platform di mana int dan long yang kedua 32 bit dan lama adalah 64 bit dan mempertimbangkan ekspresi.
(-(-2147483648) == -2147483648 )
Compiler kebutuhan untuk menemukan jenis yang dapat menampung 2147483648, pada comforming C99 compiler akan menggunakan jenis "lama" tapi C90 compiler dapat menggunakan jenis "unsigned long".
Jika compiler menggunakan tipe lama kemudian ada yang meluap dan perbandingan adalah palsu. Jika compiler menggunakan unsigned lama kemudian unsigned sampul aturan yang datang ke dalam bermain dan perbandingan adalah true.
Untuk alasan yang sama yang berkelok-kelok tape deck counter 500 langkah maju dari 000 (melalui 001 002 003 ...) akan menampilkan 500, dan berkelok-kelok itu mundur 500 langkah mundur dari 000 (melalui 999 997 998 ...) juga akan menampilkan 500.
Ini adalah dua's melengkapi notasi. Tentu saja, sejak 2's melengkapi menandatangani konvensi ini adalah untuk mempertimbangkan paling sedikit tanda sedikit, hasilnya melimpah yang dapat direpresentasikan berbagai, seperti 2000000000+2000000000 meluap representable berbagai.
Akibatnya, prosesor's "overflow" sedikit akan ditetapkan (melihat hal ini membutuhkan akses ke mesin's aritmatika bendera, umumnya tidak terjadi di sebagian besar bahasa pemrograman luar assembler). Ini adalah only nilai yang akan mengatur "overflow" sedikit ketika menegasikan 2's melengkapi jumlah: nilai lain's negasi terletak di kisaran direpresentasikan oleh 2's melengkapi.