Saya ingin mendefinisikan fungsi yang mengambil unsigned int
argumen sebagai dan mengembalikan int
modulo UINT_MAX + 1 yang kongruen ke argumen.
Upaya pertama mungkin terlihat seperti ini:
int unsigned_to_signed(unsigned n)
{
return static_cast<int>(n);
}
Tapi seperti yang diketahui pengacara bahasa, casting dari unsigned ke signed untuk nilai yang lebih besar dari INT_MAX adalah definisi implementasi.
Saya ingin menerapkan ini sedemikian rupa sehingga (a) hanya bergantung pada perilaku yang diamanatkan oleh spesifikasi; dan (b) mengkompilasi menjadi no-op pada mesin modern manapun dan mengoptimalkan compiler.
Adapun mesin aneh ... Jika tidak ada modulo kongruen int ditandatangani UINT_MAX + 1 ke int unsigned, katakanlah saya ingin melempar pengecualian. Jika ada lebih dari satu (saya tidak yakin ini mungkin), katakanlah saya ingin yang terbesar.
Oke, upaya kedua:
int unsigned_to_signed(unsigned n)
{
int int_n = static_cast<int>(n);
if (n == static_cast<unsigned>(int_n))
return int_n;
// else do something long and complicated
}
Saya tidak terlalu peduli dengan efisiensi ketika saya tidak menggunakan sistem pelengkap dua, karena menurut pendapat saya yang sederhana itu tidak mungkin. Dan jika kode saya menjadi hambatan pada sistem magnitudo tanda yang ada di mana-mana pada tahun 2050, saya yakin seseorang dapat mengetahuinya dan mengoptimalkannya saat itu.
Sekarang, upaya kedua ini hampir mendekati apa yang saya inginkan. Meskipun transmisi ke int
ditentukan oleh implementasi untuk beberapa masukan, transmisi kembali ke unsigned
dijamin oleh standar untuk mempertahankan nilai modulo UINT_MAX + 1. Jadi kondisional memeriksa dengan tepat apa yang saya inginkan, dan tidak akan terkompilasi menjadi apa pun di sistem apa pun yang mungkin saya temui.
Namun ... Saya masih mentransmisikan ke int
tanpa terlebih dahulu memeriksa apakah itu akan memanggil perilaku yang ditentukan implementasi. Pada beberapa sistem hipotetis di tahun 2050, ia dapat melakukan entah apa. Jadi katakanlah saya ingin menghindari itu.
Pertanyaan: Seperti apa tampilan "percobaan ketiga" saya?
Sebagai ringkasan, saya ingin:
- Transmisikan dari unsigned int ke signed int
- Pertahankan nilai mod UINT_MAX + 1
- Gunakan hanya perilaku yang diamanatkan standar
- Kompilasi menjadi no-op pada mesin pelengkap dua tipikal dengan pengoptimal kompiler
[Memperbarui]
Izinkan saya memberi contoh untuk menunjukkan mengapa ini bukan pertanyaan yang sepele.
Pertimbangkan implementasi hipotetis C ++ dengan properti berikut:
sizeof(int)
sama dengan 4sizeof(unsigned)
sama dengan 4INT_MAX
sama dengan 32767INT_MIN
sama dengan -2 32 + 32768UINT_MAX
sama dengan 2 32 - 1- Aritmatika aktif
int
adalah modulo 2 32 (ke dalam kisaranINT_MIN
melaluiINT_MAX
) std::numeric_limits<int>::is_modulo
adalah benar- Casting unsigned
n
to int mempertahankan nilai untuk 0 <= n <= 32767 dan sebaliknya menghasilkan nol
Pada implementasi hipotetis ini, terdapat tepat satu int
nilai kongruen (mod UINT_MAX + 1) untuk setiap unsigned
nilai. Jadi pertanyaan saya akan terdefinisi dengan baik.
Saya mengklaim bahwa implementasi C ++ hipotetis ini sepenuhnya sesuai dengan spesifikasi C ++ 98, C ++ 03, dan C ++ 11. Saya akui saya belum menghafal setiap kata dari semuanya ... Tapi saya yakin saya telah membaca bagian yang relevan dengan cermat. Jadi jika Anda ingin saya menerima jawaban Anda, Anda harus (a) mengutip spesifikasi yang mengesampingkan implementasi hipotetis ini atau (b) menanganinya dengan benar.
Memang, jawaban yang benar harus menangani setiap implementasi hipotetis yang diizinkan oleh standar. Itulah yang dimaksud dengan "hanya menjalankan perilaku yang diamanatkan standar", menurut definisi.
Secara kebetulan, catatan yang std::numeric_limits<int>::is_modulo
sama sekali tidak berguna di sini karena berbagai alasan. Untuk satu hal, bisa jadi true
bahkan jika cast unsigned-to-signed tidak berfungsi untuk nilai unsigned yang besar. Untuk yang lain, itu bisa true
bahkan pada sistem-komplemen atau besaran-tanda, jika aritmatika hanyalah modulo seluruh rentang bilangan bulat. Dan seterusnya. Jika jawaban Anda bergantung pada is_modulo
, itu salah.
[Perbarui 2]
Jawaban hvd mengajari saya sesuatu: Implementasi hipotetis C ++ saya untuk bilangan bulat tidak diizinkan oleh C modern. Standar C99 dan C11 sangat spesifik tentang representasi bilangan bulat yang ditandatangani; memang, mereka hanya mengizinkan dua-pelengkap, pelengkap satu, dan besaran tanda (bagian 6.2.6.2 ayat (2);).
Tapi C ++ bukanlah C. Ternyata, fakta ini menjadi inti dari pertanyaan saya.
Standar C ++ 98 asli didasarkan pada C89 yang jauh lebih tua, yang mengatakan (bagian 3.1.2.5):
Untuk setiap jenis bilangan bulat bertanda, ada jenis bilangan bulat unsigned yang sesuai (tetapi berbeda) (ditetapkan dengan kata kunci unsigned) yang menggunakan jumlah penyimpanan yang sama (termasuk informasi tanda) dan memiliki persyaratan penyelarasan yang sama. Rentang nilai nonnegatif dari tipe integer bertanda adalah subrange dari tipe integer unsigned yang sesuai, dan representasi nilai yang sama di setiap tipe adalah sama.
C89 tidak mengatakan apa-apa tentang hanya memiliki satu tanda bit atau hanya mengizinkan dua-pelengkap / satu-pelengkap / besaran-tanda.
Standar C ++ 98 mengadopsi bahasa ini hampir kata demi kata (bagian 3.9.1 paragraf (3)):
Untuk setiap jenis bilangan bulat bertanda, terdapat jenis bilangan bulat unsigned yang sesuai (tetapi berbeda) : "
unsigned char
", "unsigned short int
", "unsigned int
", dan "unsigned long int
", yang masing-masing menempati jumlah penyimpanan yang sama dan memiliki persyaratan penyelarasan yang sama (3.9 ) sebagai jenis bilangan bulat bertanda tangan yang sesuai; artinya, setiap tipe integer bertanda memiliki representasi objek yang sama dengan tipe integer unsigned yang sesuai . Rentang nilai nonnegatif dari tipe bilangan bulat bertanda adalah subrentang dari jenis bilangan bulat tak bertanda yang sesuai, dan representasi nilai dari setiap jenis bertanda tangan / tak bertanda tangan harus sama.
Standar C ++ 03 menggunakan bahasa yang pada dasarnya identik, seperti halnya C ++ 11.
Tidak ada spesifikasi C ++ standar yang membatasi representasi integer yang ditandatangani ke spesifikasi C apa pun, sejauh yang saya tahu. Dan tidak ada yang mengamanatkan sedikit pun tanda atau semacamnya. Semua yang dikatakan adalah bahwa bilangan bulat bertanda non-negatif harus menjadi subrentang dari unsigned yang sesuai.
Jadi, sekali lagi saya mengklaim bahwa INT_MAX = 32767 dengan INT_MIN = -2 32 +32768 diizinkan. Jika jawaban Anda mengasumsikan sebaliknya, itu tidak benar kecuali Anda mengutip standar C ++ yang membuktikan bahwa saya salah.