Saya telah menemukan kode dari seseorang yang tampaknya percaya ada masalah mengurangi bilangan bulat unsigned dari bilangan bulat lain dari jenis yang sama ketika hasilnya akan negatif. Jadi kode seperti ini tidak akan benar meskipun kebetulan berfungsi pada sebagian besar arsitektur.
unsigned int To, Tf;
To = getcounter();
while (1) {
Tf = getcounter();
if ((Tf-To) >= TIME_LIMIT) {
break;
}
}
Ini adalah satu-satunya kutipan relevan yang samar-samar dari standar C yang dapat saya temukan.
Perhitungan yang melibatkan operand unsigned tidak akan pernah bisa overflow, karena hasil yang tidak dapat diwakili oleh jenis unsigned integer yang dihasilkan dikurangi modulo bilangan yang satu lebih besar dari nilai terbesar yang dapat diwakili oleh jenis yang dihasilkan.
Saya kira orang bisa mengambil kutipan itu berarti bahwa ketika operan kanan lebih besar operasi disesuaikan agar bermakna dalam konteks nomor terpotong modulo.
yaitu
0x0000 - 0x0001 == 0x 1 0000 - 0x0001 == 0xFFFF
sebagai kebalikan dari penggunaan semantik bertanda tangan yang bergantung pada implementasi:
0x0000 - 0x0001 == (unsigned) (0 + -1) == (0xFFFF tetapi juga 0xFFFE atau 0x8001)
Manakah atau interpretasi apa yang benar? Apakah itu didefinisikan sama sekali?
Jawaban:
Hasil pengurangan yang menghasilkan bilangan negatif dalam tipe unsigned didefinisikan dengan baik:
Seperti yang Anda lihat,
(unsigned)0 - (unsigned)1
sama dengan -1 modulo UINT_MAX + 1, atau dengan kata lain, UINT_MAX.Perhatikan bahwa meskipun dikatakan "Perhitungan yang melibatkan operan unsigned tidak akan pernah bisa meluap", yang mungkin membuat Anda percaya bahwa ini hanya berlaku untuk melebihi batas atas, ini disajikan sebagai motivasi untuk bagian pengikatan sebenarnya dari kalimat: "a hasil yang tidak dapat diwakili oleh jenis bilangan bulat unsigned yang dihasilkan dikurangi modulo angka yang lebih besar dari nilai terbesar yang dapat diwakili oleh jenis yang dihasilkan. " Frasa ini tidak terbatas pada luapan batas atas tipe, dan berlaku sama untuk nilai yang terlalu rendah untuk diwakili.
sumber
uint
selalu dimaksudkan untuk mewakili cincin matematika dari bilangan bulat0
melaluiUINT_MAX
, dengan operasi modulo penjumlahan dan perkalianUINT_MAX+1
, dan bukan karena dari luapan. Namun, hal itu menimbulkan pertanyaan mengapa, jika cincin adalah tipe data fundamental, bahasa tersebut tidak menawarkan dukungan yang lebih umum untuk cincin dengan ukuran lain.Saat Anda bekerja dengan tipe unsigned , aritmatika modular (juga dikenal sebagai perilaku "membungkus" ) berlangsung. Untuk memahami aritmatika modular ini , lihat saja jam-jam berikut:
9 + 4 = 1 ( 13 mod 12 ), jadi untuk arah lainnya adalah: 1 - 4 = 9 ( -3 mod 12 ). Prinsip yang sama diterapkan saat bekerja dengan tipe unsigned. Jika jenis hasil adalah
unsigned
, maka aritmatika modular berlangsung.Sekarang lihat operasi berikut yang menyimpan hasil sebagai
unsigned int
:Ketika Anda ingin memastikan bahwa hasilnya adalah
signed
, simpan ke dalamsigned
variabel atau cast kesigned
. Jika Anda ingin mendapatkan perbedaan antara angka dan memastikan bahwa aritmatika modular tidak akan diterapkan, Anda harus mempertimbangkan untuk menggunakanabs()
fungsi yang ditentukan dalamstdlib.h
:Berhati-hatilah, terutama saat menulis kondisi, karena:
tapi
sumber
int d = abs(five - seven);
tidak bagus. Pertamafive - seven
dihitung: promosi meninggalkan jenis operan sebagaiunsigned int
, hasilnya dihitung modulo(UINT_MAX+1)
, dan dievaluasi keUINT_MAX-1
. Maka nilai ini adalah parameter aktual untukabs
, yaitu berita buruk.abs(int)
menyebabkan perilaku tidak terdefinisi meneruskan argumen, karena tidak dalam jangkauan, danabs(long long)
mungkin dapat menahan nilai, tetapi perilaku tak terdefinisi terjadi ketika nilai yang dikembalikan dipaksaint
untuk menginisialisasid
.operator T()
. Penambahan dua ekspresi yang kita diskusikan dilakukan dalam tipeunsigned int
, berdasarkan tipe operan. Hasil penjumlahan adalahunsigned int
. Kemudian hasil tersebut secara implisit dikonversi ke jenis yang diperlukan dalam konteks, sebuah konversi yang gagal karena nilainya tidak dapat direpresentasikan dalam jenis baru.double x = 2/3;
vsdouble y = 2.0/3;
Nah, interpretasi pertama benar. Namun, alasan Anda tentang "semantik bertanda tangan" dalam konteks ini salah.
Sekali lagi, interpretasi pertama Anda benar. Aritmatika unsigned mengikuti aturan aritmatika modulo, yang berarti
0x0000 - 0x0001
mengevaluasi ke0xFFFF
untuk jenis unsigned 32-bit.Namun, interpretasi kedua (yang didasarkan pada "semantik bertanda tangan") juga diperlukan untuk menghasilkan hasil yang sama. Yaitu bahkan jika Anda mengevaluasi
0 - 1
dalam domain tipe bertanda tangan dan mendapatkan-1
sebagai hasil perantara, ini-1
masih diperlukan untuk menghasilkan0xFFFF
ketika nanti diubah ke tipe tak bertanda. Bahkan jika beberapa platform menggunakan representasi eksotis untuk bilangan bulat bertanda (komplemen 1, besaran bertanda), platform ini masih diharuskan untuk menerapkan aturan aritmatika modulo saat mengonversi nilai bilangan bulat bertanda ke yang tidak bertanda.Misalnya evaluasi ini
masih dijamin untuk menghasilkan
UINT_MAX
dic
, bahkan jika platform ini menggunakan representasi eksotis untuk bilangan bulat ditandatangani.sumber
Dengan nomor unsigned dari jenis
unsigned int
atau lebih besar, dengan tidak adanya konversi jenis,a-b
didefinisikan sebagai menghasilkan nomor unsigned yang, bila ditambahkanb
, akan menghasilkana
. Konversi bilangan negatif menjadi unsigned didefinisikan sebagai menghasilkan bilangan yang, jika ditambahkan ke bilangan asli yang dibalik tanda, akan menghasilkan nol (jadi mengubah -5 menjadi unsigned akan menghasilkan nilai yang, jika ditambahkan ke 5, akan menghasilkan nol) .Perhatikan bahwa angka unsigned lebih kecil dari yang
unsigned int
dapat dipromosikan menjadi tipeint
sebelum pengurangan, perilakua-b
akan tergantung pada ukuranint
.sumber