Mengapa Java tidak menyertakan dukungan untuk bilangan bulat tak bertanda?
Bagi saya kelihatannya merupakan kelalaian aneh, mengingat bahwa mereka memungkinkan seseorang untuk menulis kode yang cenderung menghasilkan kelebihan pada input besar yang tidak terduga.
Lebih jauh lagi, menggunakan bilangan bulat yang tidak ditandatangani dapat menjadi bentuk dokumentasi diri, karena mereka menunjukkan bahwa nilai yang ingin disimpan oleh unsigned int tidak pernah dianggap negatif.
Terakhir, dalam beberapa kasus, bilangan bulat tak bertanda dapat lebih efisien untuk operasi tertentu, seperti divisi.
Apa kerugiannya termasuk ini?
java
language-design
unsigned
integer
dsimcha
sumber
sumber
byte
tidak bisa memberikan140
tingkat abu-abu lurus tetapi-116
yang Anda butuhkan& 0xff
untuk mendapatkan nilai yang benar.Jawaban:
Ini dari wawancara dengan Gosling dan lainnya , tentang kesederhanaan:
sumber
Membaca yang tersirat, saya pikir logikanya seperti ini:
Sebagian besar, saya akan mengatakan itu adalah keputusan yang masuk akal. Mungkin, saya akan memiliki:
Namun, dengan sedikit kludging, operasi pada nilai yang tidak ditandatangani hingga 32 bit tidak terlalu buruk, dan kebanyakan orang tidak perlu pembagian atau perbandingan 64-bit yang tidak ditandatangani.
sumber
short
digunakan - algoritma defltate / gzip / inflate adalah 16bit dan mereka sangat bergantung pada celana pendek ... atau setidaknyashort[]
[diakui itu asli - namun java impl dari algoritma ini membawa terrabytes data]. Yang terakhir (short[]
) memiliki keuntungan yang signifikanint[]
karena membutuhkan memori dua kali lebih sedikit dan lebih sedikit memori = properti caching yang lebih baik, kinerja yang jauh lebih baik.Ini adalah pertanyaan yang lebih tua dan tepuk memang menyebutkan char, saya hanya berpikir saya harus memperluas ini untuk orang lain yang akan melihat ini di jalan. Mari kita lihat lebih dekat tipe-tipe primitif Java:
byte
- Bilangan bulat bertanda 8-bitshort
- Bilangan bulat bertanda 16-bitint
- Bilangan bulat bertanda 32-bitlong
- Bilangan bulat bertanda 64-bitchar
- Karakter 16-bit (bilangan bulat tak bertanda)Meskipun
char
tidak mendukungunsigned
aritmatika, pada dasarnya dapat diperlakukan sebagaiunsigned
bilangan bulat. Anda harus secara eksplisit memasukkan operasi aritmatika kembalichar
, tetapi itu memberi Anda cara untuk menentukanunsigned
angka.Ya, tidak ada dukungan langsung untuk bilangan bulat tak bertanda (jelas, saya tidak perlu mengembalikan sebagian besar operasi saya ke char jika ada dukungan langsung). Namun, tentu saja ada tipe data primitif yang tidak ditandatangani. Saya ingin melihat byte yang tidak ditandatangani juga, tapi saya kira menggandakan biaya memori dan alih-alih menggunakan char adalah pilihan yang layak.
Edit
Dengan JDK8 ada API baru untuk
Long
danInteger
yang menyediakan metode pembantu saat memperlakukanlong
danint
nilai sebagai nilai yang tidak ditandatangani.compareUnsigned
divideUnsigned
parseUnsignedInt
parseUnsignedLong
remainderUnsigned
toUnsignedLong
toUnsignedString
Selain itu, Guava menyediakan sejumlah metode pembantu untuk melakukan hal serupa pada tipe integer yang membantu menutup celah yang ditinggalkan oleh kurangnya dukungan asli untuk
unsigned
integer.sumber
char
terlalu kecil untuk mendukunglong
aritmatika, misalnya.Java memang memiliki tipe unsigned, atau setidaknya satu: char adalah unsigned short. Jadi alasan apa pun yang dilontarkan Gosling, itu hanyalah ketidaktahuannya mengapa tidak ada tipe yang tidak ditandatangani.
Juga Tipe pendek: celana pendek digunakan sepanjang waktu untuk multimedia. Alasannya adalah Anda dapat memasukkan 2 sampel dalam 32-bit unsigned lama dan banyak vektorisasi banyak operasi. Hal yang sama dengan data 8-bit dan byte yang tidak ditandatangani. Anda dapat memasukkan 4 atau 8 sampel dalam register untuk vectorisasi.
sumber
char
selain karakter.Segera setelah int yang ditandatangani dan tidak ditandatangani dicampur dalam sebuah ekspresi, hal-hal mulai menjadi berantakan dan Anda mungkin akan kehilangan informasi. Membatasi Java ke ints yang ditandatangani hanya benar-benar membersihkan segalanya. Saya senang saya tidak perlu khawatir tentang seluruh bisnis yang ditandatangani / tidak ditandatangani, meskipun kadang-kadang saya ketinggalan bit ke-8 dalam byte.
sumber
static_cast
banyak untuk mencampurnya. Memang berantakan.byte
ditandatangani seperti di Pascal.& 0xFF
setiap promosi byte-ke-int membuat kode lebih berantakan.http://skeletoncoder.blogspot.com/2006/09/java-tutorials-why-no-unsigned.html
Orang ini mengatakan karena standar C mendefinisikan operasi yang melibatkan int ditandatangani dan ditandatangani untuk diperlakukan sebagai unsigned. Ini dapat menyebabkan bilangan bulat bertanda negatif untuk berguling-guling ke int unsigned besar, berpotensi menyebabkan bug.
sumber
-1
setiap quanity yang tidak ditandatangani - bahkan nol.-1
usia "tidak diketahui" (seperti yang disarankan artikel) adalah salah satu contoh klasik "bau kode" . Misalnya, jika Anda ingin menghitung "berapa Alice lebih tua dari Bob?", Dan A = 25 dan B = -1, Anda akan mendapatkan jawaban±26
yang salah. Penanganan yang tepat dari nilai yang tidak diketahui adalahOption<TArg>
kapanSome(25) - None
akan kembaliNone
.Saya pikir Java baik-baik saja, menambahkan unsigned akan menyulitkannya tanpa banyak keuntungan. Bahkan dengan model integer yang disederhanakan, kebanyakan programmer Java tidak tahu bagaimana tipe numerik dasar berperilaku - cukup baca buku Java Puzzlers untuk melihat kesalahpahaman apa yang mungkin Anda miliki.
Adapun saran praktis:
Jika nilai Anda agak sewenang-wenang dan tidak cocok
int
, gunakanlong
. Jika tidak cocoklong
digunakanBigInteger
.Gunakan tipe yang lebih kecil hanya untuk array ketika Anda perlu menghemat ruang.
Jika Anda membutuhkan tepat 64/32/16/8 bit, gunakan
long
/int
/short
/byte
dan berhentilah mengkhawatirkan tentang bit tanda, kecuali untuk pembagian, perbandingan, shift kanan, dan casting.Lihat juga jawaban ini tentang "porting generator angka acak dari C ke Jawa".
sumber
>>
dan>>>
untuk yang ditandatangani dan yang tidak, masing-masing. Bergeser ke kiri tidak masalah.>>>
tidak berfungsi untukshort
danbyte
. Misalnya,(byte)0xff>>>1
hasilkan0x7fffffff
bukan0x7f
. Contoh lain:byte b=(byte)0xff; b>>>=1;
akan menghasilkanb==(byte)0xff
. Tentu saja Anda dapat melakukannyab=(byte)(b & 0xff >> 1);
tetapi ini menambah satu operasi lagi (bitwise &).Dengan JDK8 itu memang memiliki beberapa dukungan untuk mereka.
Kita mungkin melihat dukungan penuh dari tipe yang tidak ditandatangani di Jawa terlepas dari kekhawatiran Gosling.
sumber
Saya tahu posting ini terlalu lama; namun untuk minat Anda, di Java 8 dan yang lebih baru, Anda dapat menggunakan
int
tipe data untuk mewakili integer 32-bit yang tidak ditandatangani, yang memiliki nilai minimum 0 dan nilai maksimum 2 32 −1. GunakanInteger
kelas untuk menggunakanint
tipe data sebagai bilangan bulat yang tidak ditandai dan metode statis seperticompareUnsigned()
,divideUnsigned()
dll. Telah ditambahkan keInteger
kelas untuk mendukung operasi aritmatika untuk bilangan bulat yang tidak ditandai.sumber
Saya pernah mendengar cerita bahwa mereka akan dimasukkan dekat dengan rilis Jawa orignal. Oak adalah pendahulu untuk Java, dan dalam beberapa dokumen spesifikasi disebutkan nilai-nilai usigned. Sayangnya ini tidak pernah berhasil masuk ke dalam bahasa Jawa. Sejauh siapa pun bisa mengetahui mereka hanya tidak diimplementasikan, kemungkinan karena kendala waktu.
sumber
char
) ditinggalkan karena para perancang pikir itu adalah ide yang buruk ... diberikan tujuan bahasa.Saya pernah mengikuti kursus C ++ dengan seseorang di komite standar C ++ yang menyiratkan bahwa Java membuat keputusan yang tepat untuk menghindari bilangan bulat yang tidak ditandai karena (1) sebagian besar program yang menggunakan bilangan bulat yang tidak ditandatangani dapat dilakukan dengan baik dengan bilangan bulat yang ditandatangani dan ini lebih alami di dalam hal cara orang berpikir, dan (2) menggunakan bilangan bulat yang tidak ditandatangani menghasilkan banyak hal yang mudah dibuat tetapi sulit untuk men-debug masalah seperti bilangan bulat aritmatika bilangan bulat dan kehilangan bit signifikan ketika mengkonversi antara jenis yang ditandatangani dan yang tidak ditandatangani. Jika Anda salah mengurangi 1 dari 0 menggunakan bilangan bulat yang ditandatangani, sering kali program Anda lebih cepat macet dan membuatnya lebih mudah untuk menemukan bug daripada membungkusnya menjadi 2 ^ 32 - 1, dan kompiler serta alat analisis statis dan pemeriksaan runtime harus menganggap Anda tahu apa yang Anda lakukan karena Anda memilih untuk menggunakan aritmatika yang tidak ditandatangani. Juga,
Dulu, ketika memori terbatas dan prosesor tidak secara otomatis beroperasi pada 64 bit sekaligus, setiap bit menghitung lebih banyak, jadi setelah menandatangani vs unsigned byte atau celana pendek sebenarnya lebih penting dan jelas keputusan desain yang tepat. Saat ini hanya menggunakan int yang ditandatangani sudah lebih dari cukup di hampir semua kasus pemrograman reguler, dan jika program Anda benar-benar perlu menggunakan nilai yang lebih besar dari 2 ^ 31 - 1, Anda seringkali tetap menginginkan yang lama. Setelah Anda berada di wilayah penggunaan long, bahkan lebih sulit untuk menemukan alasan mengapa Anda benar-benar tidak dapat bertahan dengan 2 ^ 63 - 1 bilangan bulat positif. Setiap kali kita pergi ke prosesor 128 bit itu akan menjadi lebih sedikit masalah.
sumber
Pertanyaan Anda adalah "Mengapa Java tidak mendukung int yang tidak ditandatangani"?
Dan jawaban saya untuk pertanyaan Anda adalah bahwa Java ingin semua tipe primitifnya: byte , char , short , int dan long harus diperlakukan sebagai byte , word , dword dan qword , persis seperti dalam assembly, dan operator Java ditandatangani operasi pada semua itu tipe primitif kecuali untuk char , tetapi hanya pada char mereka tidak ditandatangani 16 bit saja.
Jadi metode statis seharusnya menjadi operasi yang tidak ditandatangani juga untuk 32 dan 64 bit.
Anda memerlukan kelas final, yang metode statisnya dapat dipanggil untuk operasi yang tidak ditandatangani .
Anda dapat membuat kelas akhir ini, sebut saja nama apa pun yang Anda inginkan dan terapkan metode statisnya.
Jika Anda tidak tahu cara menerapkan metode statis maka tautan ini dapat membantu Anda.
Menurut pendapat saya, Java tidak sama dengan C ++ sama sekali , jika tidak mendukung tipe yang tidak ditandatangani atau overloading operator, jadi saya pikir Java harus diperlakukan sebagai bahasa yang sama sekali berbeda dari C ++ dan dari C.
Ngomong-ngomong, bahasa ini juga sangat berbeda dalam hal bahasa.
Jadi saya tidak merekomendasikan di Java untuk mengetikkan kode yang mirip dengan C dan saya tidak merekomendasikan untuk mengetikkan kode yang mirip dengan C ++ sama sekali, karena di Jawa Anda tidak akan bisa melakukan apa yang ingin Anda lakukan selanjutnya di C ++, yaitu kode tidak akan terus menjadi C ++ sama sekali dan bagi saya ini buruk untuk kode seperti itu, untuk mengubah gaya di tengah.
Saya sarankan untuk menulis dan menggunakan metode statis juga untuk operasi yang ditandatangani, jadi Anda tidak melihat dalam campuran kode operator dan metode statis untuk operasi yang ditandatangani dan tidak ditandatangani, kecuali jika Anda hanya perlu menandatangani operasi dalam kode, dan tidak apa-apa untuk gunakan operator saja.
Juga saya sarankan untuk menghindari menggunakan pendek , int dan panjang tipe primitif, dan penggunaan kata , dword dan QWORD masing-masing sebagai gantinya, dan Anda tentang memanggil metode statis untuk operasi ditandatangani dan / atau menandatangani operasi daripada menggunakan operator.
Jika Anda akan melakukan operasi yang ditandatangani saja dan menggunakan operator hanya dalam kode, maka ini boleh saja untuk menggunakan tipe primitif ini pendek , int dan panjang .
Sebenarnya kata , dword dan QWORD jangan tidak ada dalam bahasa, tetapi Anda dapat membuat kelas baru untuk masing-masing dan pelaksanaan setiap harus sangat mudah:
Kelas kata memegang tipe primitif pendek saja, kelas dword memegang tipe primitif int saja dan kelas QWORD memegang jenis primitif panjang saja. Sekarang semua unsigned dan metode yang ditandatangani sebagai statis atau tidak sebagai pilihan Anda, Anda dapat menerapkan di setiap kelas, yaitu semua operasi 16 bit baik ditandatangani dan ditandatangani dengan memberikan nama makna pada kelas kata , semua operasi 32 bit baik unsigned dan ditandatangani dengan memberikan nama makna pada kelas kata sandi dan semua operasi 64 bit baik ditandatangani dan ditandatangani dengan memberikan nama makna pada kelas kata sandi .
Jika Anda tidak suka memberikan terlalu banyak nama yang berbeda untuk setiap metode, Anda selalu dapat menggunakan kelebihan beban di Jawa, bagus untuk membaca bahwa Java tidak menghapus itu juga!
Jika Anda menginginkan metode alih-alih operator untuk operasi bertanda tangan 8 bit dan metode untuk operasi tanpa tanda tangan 8 bit yang tidak memiliki operator sama sekali, maka Anda dapat membuat kelas Byte (perhatikan bahwa huruf pertama 'B' adalah modal, jadi ini bukan tipe byte primitif ) dan mengimplementasikan metode di kelas ini.
Tentang lewat nilai dan lewat referensi:
Jika saya tidak salah, seperti di C #, benda primitif yang disahkan oleh nilai alamiah, tetapi benda-benda kelas tersebut diteruskan oleh referensi secara alami, sehingga berarti bahwa objek tipe Byte , kata , dword dan QWORD akan dikirimkan dengan referensi dan bukan oleh nilai secara default. Saya berharap Java memiliki objek struct seperti yang dimiliki C #, jadi semua Byte , word , dword , dan qword dapat diimplementasikan menjadi struct, bukan kelas, jadi secara default mereka dilewatkan oleh nilai dan bukan oleh referensi secara default, seperti objek struct apa pun di C #, seperti tipe primitif, dilewatkan oleh nilai dan bukan oleh referensi secara default, tetapi karena Java lebih buruk daripada C # dan kami memiliki untuk mengatasinya, maka hanya ada kelas dan antarmuka, yang dilewatkan oleh referensi dan bukan oleh nilai secara default. Jadi, jika Anda ingin meneruskan objek Byte , word , dword , dan qword dengan nilai dan bukan dengan referensi, seperti objek kelas lainnya di Jawa dan juga dalam C #, Anda harus cukup menggunakan copy constructor dan hanya itu.
Itulah satu-satunya solusi yang dapat saya pikirkan. Saya hanya berharap bahwa saya bisa mengetikkan tipe primitif ke kata, kata dan qword, tetapi Java tidak mendukung typedef atau menggunakan sama sekali, tidak seperti C # yang mendukung penggunaan , yang setara dengan typedef C.
Tentang output:
Untuk urutan bit yang sama , Anda dapat mencetaknya dengan banyak cara: Sebagai biner, sebagai desimal (seperti arti% u dalam C printf), sebagai oktal (seperti arti% o dalam C printf), sebagai heksadesimal (seperti arti% x dalam printf C) dan sebagai integer (seperti arti dari% d dalam printf C).
Perhatikan bahwa C printf tidak tahu tipe variabel yang diteruskan sebagai parameter ke fungsi, jadi printf tahu tipe setiap variabel hanya dari objek char * yang diteruskan ke parameter pertama dari fungsi.
Jadi di masing-masing kelas: Byte , word , dword dan qword , Anda dapat menerapkan metode cetak dan mendapatkan fungsionalitas printf, meskipun tipe primitif dari kelas tersebut ditandatangani, Anda masih dapat mencetaknya sebagai unsigned dengan mengikuti beberapa algoritma yang melibatkan operasi logis dan bergeser untuk mendapatkan digit untuk mencetak ke output.
Sayangnya tautan yang saya berikan kepada Anda tidak menunjukkan cara menerapkan metode cetak ini, tetapi saya yakin Anda dapat google untuk algoritma yang Anda butuhkan untuk menerapkan metode cetak ini.
Hanya itu yang bisa saya jawab pertanyaan Anda dan sarankan Anda.
sumber
Karena
unsigned
tipe adalah kejahatan murni.Fakta bahwa dalam C
unsigned - int
menghasilkanunsigned
bahkan lebih jahat.Ini adalah snapshot dari masalah yang membakar saya lebih dari sekali:
Sudahkah Anda memperhatikan bug itu? Saya akui saya baru melihatnya setelah masuk dengan debugger.
Karena
n
tipe unsignedsize_t
seluruh ekspresin - (rays.size() - 1) / 2
dievaluasi sebagaiunsigned
. Ekspresi itu dimaksudkan untuk menjadi posisi yang ditandatangani darin
sinar th dari yang tengah: sinar ke-1 dari yang tengah di sisi kiri akan memiliki posisi -1, yang pertama di sebelah kanan akan memiliki posisi +1, dll. Setelah mengambil nilai abs dan mengalikannya dengandelta
sudut saya akan mendapatkan sudut antaran
sinar th dan yang tengah.Sayangnya bagi saya ungkapan di atas mengandung kejahatan yang tidak ditandatangani dan alih-alih mengevaluasi, katakan, -1, ia mengevaluasi menjadi 2 ^ 32-1. Konversi berikutnya untuk
double
menutup bug.Setelah satu atau dua bug yang disebabkan oleh penyalahgunaan
unsigned
aritmatika kita harus mulai bertanya-tanya apakah bit tambahan yang didapat sebanding dengan masalah ekstra. Saya berusaha, sedapat mungkin, untuk menghindari penggunaanunsigned
tipe dalam aritmatika, meskipun masih menggunakannya untuk operasi non-aritmatika seperti topeng biner.sumber
unsigned
dikonversi keint
setiap operasi, apa gunanyaunsigned
? Itu tidak akan memiliki fungsi yang dapat dibedakan darishort
. Dan jika Anda mengonversiint
hanya pada operasi campuran, sepertiunsigned+int
atauunsigned+float
, maka Anda masih memiliki masalah((unsigned)25-(unsigned)30)*1.0 > 0
, yang merupakan penyebab utamaunsigned
bug terkait.exit(1);
benar - benar 'sepadan dengan masalah ekstra'? Apakah tidak dapat membuka file besar benar-benar layak untuk keamanan yang programmer java kurang berpengalaman tidak akan mengacaukan menggunakanunsigned
?n - (rays.size() - 1) / 2
. Anda harus selalu memberi tanda kurung pada operator biner karena pembaca kode tidak perlu berasumsi tentang urutan operasi dalam program komputer. Hanya karena kami secara konvensional mengatakan a + b c = a + (b c) tidak berarti Anda dapat menganggap ini ketika membaca kode. Selanjutnya, perhitungan harus didefinisikan di luar loop sehingga dapat diuji tanpa loop hadir. Ini adalah bug karena tidak memastikan tipe Anda berbaris dan bukan masalah bilangan bulat yang tidak ditandatangani. Dalam C terserah Anda untuk memastikan jenis Anda berbaris.Ada beberapa permata dalam spesifikasi 'C' yang dijatuhkan Java karena alasan pragmatis tetapi yang perlahan-lahan merayap kembali dengan permintaan pengembang (penutupan, dll).
Saya menyebutkan yang pertama karena terkait dengan diskusi ini; kepatuhan nilai pointer ke aritmatika integer tak bertanda. Dan, terkait dengan topik utas ini, sulitnya mempertahankan semantik yang tidak ditandatangani di dunia Jawa yang Ditandatangani.
Saya akan menebak jika seseorang mendapatkan alter ego Dennis Ritchie untuk memberi saran kepada tim desain Gosling, itu akan menyarankan memberi Signed sebuah "nol pada tak terbatas", sehingga semua permintaan penggantian alamat pertama-tama akan menambahkan ALGEBRAIC RING SIZE mereka untuk meniadakan nilai negatif.
Dengan begitu, setiap offset yang dilemparkan ke array tidak akan pernah menghasilkan SEGFAULT. Misalnya dalam kelas enkapsulasi yang saya sebut RingArray dari ganda yang membutuhkan perilaku tidak ditandatangani - dalam konteks "putaran otomatis":
RingArray di atas tidak akan pernah 'dapatkan' dari indeks negatif, bahkan jika pemohon jahat mencoba. Ingat, ada juga banyak permintaan sah untuk meminta nilai indeks sebelumnya (negatif).
NB:% luar modulus de-referensi permintaan yang sah sedangkan bagian dalam% modulus menutupi kebencian terang-terangan dari negatif lebih negatif daripada -modulus. Jika ini pernah muncul di Java + .. + 9 || 8 + .. + spec, maka masalahnya akan benar-benar menjadi 'programmer yang tidak bisa "memutar sendiri" KESALAHAN'.
Saya yakin apa yang disebut Java unsigned int 'defisiensi' dapat disesuaikan dengan one-liner di atas.
PS: Hanya untuk memberi konteks pada tata cara RingArray di atas, inilah operasi 'set' kandidat untuk mencocokkan operasi elemen 'get' di atas:
sumber
Saya dapat memikirkan satu efek samping yang tidak menguntungkan. Dalam basis data tertanam java, jumlah id yang dapat Anda miliki dengan bidang id 32bit adalah 2 ^ 31, bukan 2 ^ 32 (~ 2billion, bukan ~ 4billion).
sumber
Alasan IMHO adalah karena mereka terlalu malas untuk mengimplementasikan / memperbaiki kesalahan itu. Menyarankan bahwa programmer C / C ++ tidak mengerti unsigned, struktur, union, bit flag ... Hanya tidak masuk akal.
Eter Anda sedang berbicara dengan programmer dasar / bash / java di ambang memulai pemrograman ala C, tanpa pengetahuan nyata bahasa ini atau Anda hanya berbicara keluar dari pikiran Anda sendiri. ;)
ketika Anda berurusan setiap hari dalam format baik dari file atau perangkat keras Anda mulai mempertanyakan, apa yang mereka pikirkan.
Contoh yang baik di sini adalah mencoba menggunakan byte yang tidak ditandai sebagai loop yang berputar sendiri. Bagi Anda yang tidak mengerti kalimat terakhir, bagaimana mungkin Anda menyebut diri Anda seorang programmer.
DC
sumber