Bagaimana Java menangani integer underflow dan overflow?
Mengarahinya, bagaimana Anda memeriksa / menguji apakah ini terjadi?
java
integer
integer-overflow
KushalP
sumber
sumber
checked
sejauh yang saya tahu. Saya tidak melihat itu banyak digunakan, dan mengetikchecked { code; }
adalah tentang sebanyak pekerjaan memanggil metode.csc /checked ...
atau atur properti di panel properti proyek di Visual Studio.Jawaban:
Jika meluap, itu akan kembali ke nilai minimum dan berlanjut dari sana. Jika underflow, kembali ke nilai maksimum dan berlanjut dari sana.
Anda dapat memeriksanya sebelumnya sebagai berikut:
(Anda dapat menggantinya
int
denganlong
melakukan pemeriksaan yang sama untuklong
)Jika Anda berpikir bahwa ini dapat terjadi lebih dari sering, maka pertimbangkan untuk menggunakan tipe data atau objek yang dapat menyimpan nilai yang lebih besar, misalnya
long
atau mungkinjava.math.BigInteger
. Yang terakhir tidak meluap, praktis, memori JVM yang tersedia adalah batasnya.Jika Anda sudah menggunakan Java8, maka Anda dapat menggunakan metode baru
Math#addExact()
danMath#subtractExact()
yang akan menyebabkanArithmeticException
overflow.Kode sumber dapat ditemukan di sini dan di sini masing-masing.
Tentu saja, Anda juga bisa langsung menggunakannya daripada menyembunyikannya dalam
boolean
metode utilitas.sumber
+100, -100
, masing-masing. Jika Anda menambahkan satu ke integer Java, prosesnya akan terlihat seperti ini saat meluap.98, 99, 100, -100, -99, -98, ...
. Apakah itu lebih masuk akal?Math#addExact
adalah sintaks yang biasanya digunakan saat menulis javadocs - sementara biasanya itu akan dikonversi keMath.addExact
, kadang-kadang bentuk lain hanya bertahanIf it underflows, it goes back to the maximum value and continues from there.
- Anda tampaknya bingung dengan aliran bawah negatif. underflow dalam bilangan bulat terjadi setiap saat (ketika hasilnya adalah sebagian kecil).Nah, sejauh tipe integer primitif berjalan, Java tidak menangani Over / Underflow sama sekali (untuk float dan double behavior berbeda, ia akan menyiram ke +/- infinity seperti yang diamanatkan oleh IEEE-754).
Saat menambahkan dua int, Anda tidak akan mendapatkan indikasi kapan terjadi overflow. Metode sederhana untuk memeriksa overflow adalah dengan menggunakan tipe lebih besar berikutnya untuk benar-benar melakukan operasi dan memeriksa apakah hasilnya masih dalam kisaran untuk tipe sumber:
Apa yang akan Anda lakukan sebagai pengganti klausa throw, tergantung pada persyaratan aplikasi Anda (throw, flush to min / max atau hanya login apa saja). Jika Anda ingin mendeteksi kelebihan pada operasi yang lama, Anda kurang beruntung dengan primitif, gunakan BigInteger sebagai gantinya.
Sunting (2014-05-21): Karena pertanyaan ini tampaknya cukup sering dirujuk dan saya harus menyelesaikan masalah yang sama, cukup mudah untuk mengevaluasi kondisi overflow dengan metode yang sama CPU akan menghitung flag V-nya.
Pada dasarnya ini adalah ekspresi boolean yang melibatkan tanda dari kedua operan serta hasilnya:
Dalam java lebih mudah untuk menerapkan ekspresi (dalam if) ke seluruh 32 bit, dan periksa hasilnya menggunakan <0 (ini akan secara efektif menguji bit tanda). Prinsip kerjanya persis sama untuk semua tipe primitif integer , mengubah semua deklarasi dalam metode di atas menjadi lama membuatnya bekerja lama.
Untuk jenis yang lebih kecil, karena konversi implisit ke int (lihat JLS untuk operasi bitwise untuk detail), alih-alih memeriksa <0, pemeriksaan perlu menutupi bit tanda secara eksplisit (0x8000 untuk operan pendek, 0x80 untuk operan byte, sesuaikan gips dan deklarasi parameter dengan tepat):
(Perhatikan bahwa contoh di atas menggunakan kebutuhan ekspresi untuk mengurangi deteksi luapan)
Jadi bagaimana / mengapa ungkapan boolean ini bekerja? Pertama, beberapa pemikiran logis mengungkapkan bahwa luapan hanya dapat terjadi jika tanda-tanda dari kedua argumen itu sama. Karena, jika satu argumen negatif dan satu positif, hasil (dari tambah) harus mendekati nol, atau dalam kasus ekstrim satu argumen adalah nol, sama dengan argumen lainnya. Karena argumen sendiri tidak dapat membuat kondisi overflow, jumlah mereka juga tidak dapat membuat overflow.
Jadi apa yang terjadi jika kedua argumen memiliki tanda yang sama? Mari kita lihat kasus keduanya positif: menambahkan dua argumen yang membuat jumlah lebih besar dari jenis MAX_VALUE, akan selalu menghasilkan nilai negatif, sehingga terjadi overflow jika arg1 + arg2> MAX_VALUE. Sekarang nilai maksimum yang bisa dihasilkan adalah MAX_VALUE + MAX_VALUE (kasus ekstrem kedua argumen adalah MAX_VALUE). Untuk byte (contoh) yang berarti 127 + 127 = 254. Melihat representasi bit dari semua nilai yang dapat dihasilkan dari menambahkan dua nilai positif, orang menemukan bahwa yang overflow (128 hingga 254) semuanya memiliki bit 7 set, sementara semua yang tidak meluap (0 hingga 127) telah dihapus bit 7 (paling atas, tanda). Itulah tepatnya yang diperiksa bagian pertama (kanan) dari ekspresi:
(~ s & ~ d & r) menjadi benar, hanya jika , kedua operan (s, d) positif dan hasilnya (r) negatif (ekspresi bekerja pada semua 32 bit, tetapi hanya bit yang kami tertarik adalah bit paling atas (tanda), yang diperiksa terhadap <0).
Sekarang jika kedua argumen negatif, jumlah mereka tidak akan pernah bisa mendekati nol daripada argumen mana pun, jumlahnya harus lebih dekat ke minus tanpa batas. Nilai paling ekstrem yang dapat kami hasilkan adalah MIN_VALUE + MIN_VALUE, yang (sekali lagi sebagai contoh byte) menunjukkan bahwa untuk setiap nilai rentang (-1 hingga -128) bit tanda diatur, sementara kemungkinan nilai melimpah yang lain (-129 hingga -256 ) memiliki tanda sedikit dihapus. Jadi tanda hasil lagi mengungkapkan kondisi melimpah. Itulah yang setengah bagian kiri (s & d & r) memeriksa untuk kasus di mana kedua argumen (s, d) negatif dan hasil yang positif. Logikanya sebagian besar setara dengan kasus positif; semua pola bit yang dapat dihasilkan dari menambahkan dua nilai negatif akan memiliki bit tanda dihapus jika dan hanya jika terjadi underflow.
sumber
Secara default, matematika panjang dan int Java secara diam-diam membungkus overflow dan underflow. (Operasi integer pada tipe integer lain dilakukan dengan terlebih dahulu mempromosikan operan ke int atau panjang, sesuai JLS 4.2.2 .)
Sebagai Jawa 8,
java.lang.Math
menyediakanaddExact
,subtractExact
,multiplyExact
,incrementExact
,decrementExact
dannegateExact
metode statis untuk kedua int dan argumen panjang yang melakukan operasi bernama, melemparkan ArithmeticException overflow. (Tidak ada metode divideExact - Anda harus memeriksa sendiri satu case khusus (MIN_VALUE / -1
).)Pada Java 8, java.lang.Math juga menyediakan
toIntExact
untuk melemparkan panjang ke int, melempar ArithmeticException jika nilai long tidak cocok dengan int. Ini bisa berguna untuk misalnya menghitung jumlah int menggunakan matematika panjang yang tidak dicentang, kemudian menggunakantoIntExact
untuk melemparkan ke int pada akhirnya (tapi hati-hati jangan sampai jumlah Anda melimpah)Jika Anda masih menggunakan versi Java yang lebih lama, Google Guava menyediakan metode statis IntMath dan LongMath untuk penambahan, pengurangan, penggandaan, dan eksponansi yang dicentang (melempar meluap). Kelas-kelas ini juga menyediakan metode untuk menghitung faktorial dan koefisien binomial yang kembali
MAX_VALUE
pada overflow (yang kurang nyaman untuk diperiksa). Kelas utilitas primitif jambu ini,SignedBytes
,UnsignedBytes
,Shorts
danInts
, menyediakancheckedCast
metode untuk mempersempit jenis yang lebih besar (melemparkan IllegalArgumentException di bawah / overflow, tidak ArithmeticException), sertasaturatingCast
metode yang kembaliMIN_VALUE
atauMAX_VALUE
overflow.sumber
Java tidak melakukan apa pun dengan integer overflow untuk tipe primitif int atau panjang dan mengabaikan overflow dengan integer positif dan negatif.
Jawaban ini pertama menggambarkan bilangan bulat bilangan bulat, memberikan contoh bagaimana hal itu bisa terjadi, bahkan dengan nilai-nilai menengah dalam evaluasi ekspresi, dan kemudian memberikan tautan ke sumber daya yang memberikan teknik terperinci untuk mencegah dan mendeteksi bilangan bulat bilangan bulat.
Aritmatika dan ekspresi integer yang menghasilkan limpahan yang tidak terduga atau tidak terdeteksi adalah kesalahan pemrograman yang umum. Overflow integer yang tidak terduga atau tidak terdeteksi juga merupakan masalah keamanan yang dapat dieksploitasi yang terkenal, terutama karena memengaruhi objek array, stack, dan daftar.
Overflow dapat terjadi dalam arah positif atau negatif di mana nilai positif atau negatif akan melampaui nilai maksimum atau minimum untuk tipe primitif yang bersangkutan. Overflow dapat terjadi dalam nilai menengah selama ekspresi atau evaluasi operasi dan memengaruhi hasil ekspresi atau operasi di mana nilai akhir diharapkan berada dalam kisaran.
Kadang-kadang overflow negatif secara keliru disebut underflow. Underflow adalah apa yang terjadi ketika suatu nilai lebih dekat ke nol daripada yang diizinkan oleh representasi. Underflow terjadi dalam bilangan bulat aritmatika dan diharapkan. Underflow integer terjadi ketika evaluasi integer antara -1 dan 0 atau 0 dan 1. Apa yang akan menjadi hasil fraksional terpotong menjadi 0. Ini normal dan diharapkan dengan bilangan aritmatika integer dan tidak dianggap sebagai kesalahan. Namun, hal itu dapat menyebabkan pelemparan kode pengecualian. Salah satu contoh adalah pengecualian "ArithmeticException: / by nol" jika hasil dari integer underflow digunakan sebagai pembagi dalam ekspresi.
Pertimbangkan kode berikut:
yang menghasilkan x ditugaskan 0 dan evaluasi selanjutnya dari bigValue / x melempar pengecualian, "ArithmeticException: / by zero" (yaitu, bagi dengan nol), alih-alih Anda ditugaskan nilai 2.
Hasil yang diharapkan untuk x akan menjadi 858.993.458 yang kurang dari nilai int maksimum 2.147.483.647. Namun, hasil antara dari mengevaluasi Integer.MAX_Value * 2, akan menjadi 4.294.967.294, yang melebihi nilai int maksimum dan -2 sesuai dengan representasi integer komplemen 2s. Evaluasi selanjutnya dari -2 / 5 mengevaluasi ke 0 yang ditugaskan ke x.
Menyusun ulang ekspresi untuk komputasi x ke ekspresi yang, ketika dievaluasi, membagi sebelum mengalikan, kode berikut:
hasil dalam x ditugaskan 858.993.458 dan y ditugaskan 2, yang diharapkan.
Hasil antara dari bigValue / 5 adalah 429.496.729 yang tidak melebihi nilai maksimum untuk int. Evaluasi selanjutnya dari 429.496.729 * 2 tidak melebihi nilai maksimum untuk int dan hasil yang diharapkan ditugaskan ke x. Evaluasi untuk y kemudian tidak dibagi dengan nol. Evaluasi untuk x dan y berfungsi seperti yang diharapkan.
Nilai integer Java disimpan sebagai dan berperilaku sesuai dengan representasi bilangan bulat bertanda tangan 2s yang melengkapi. Ketika nilai yang dihasilkan akan lebih besar atau lebih kecil dari nilai integer maksimum atau minimum, nilai integer komplemen 2 akan menghasilkan. Dalam situasi yang tidak secara khusus dirancang untuk menggunakan perilaku komplemen 2s, yang merupakan situasi aritmatika integer paling umum, nilai komplemen 2s yang dihasilkan akan menyebabkan logika pemrograman atau kesalahan komputasi seperti yang ditunjukkan pada contoh di atas. Artikel Wikipedia yang sangat bagus menjelaskan 2s bilangan bulat biner di sini: Pelengkap dua - Wikipedia
Ada teknik untuk menghindari overflow bilangan bulat yang tidak disengaja. Teknologi dapat dikategorikan sebagai menggunakan pengujian pra-kondisi, upcasting dan BigInteger.
Pengujian pra-kondisi terdiri dari memeriksa nilai-nilai yang masuk ke operasi aritmatika atau ekspresi untuk memastikan bahwa tidak terjadi overflow dengan nilai-nilai tersebut. Pemrograman dan desain perlu membuat pengujian yang memastikan nilai input tidak akan menyebabkan overflow dan kemudian menentukan apa yang harus dilakukan jika nilai input terjadi yang akan menyebabkan overflow.
Upcasting terdiri dari penggunaan tipe primitif yang lebih besar untuk melakukan operasi atau ekspresi aritmatika dan kemudian menentukan apakah nilai yang dihasilkan di luar nilai maksimum atau minimum untuk integer. Bahkan dengan upcasting, masih mungkin bahwa nilai atau beberapa nilai menengah dalam operasi atau ekspresi akan melampaui nilai maksimum atau minimum untuk jenis upcast dan menyebabkan overflow, yang juga tidak akan terdeteksi dan akan menyebabkan hasil yang tidak diharapkan dan tidak diinginkan. Melalui analisis atau pra-kondisi, dimungkinkan untuk mencegah kelebihan dengan upcasting ketika pencegahan tanpa upcasting tidak mungkin atau praktis. Jika bilangan bulat yang dimaksud adalah tipe primitif yang panjang, maka upcasting tidak dimungkinkan dengan tipe primitif di Jawa.
Teknik BigInteger terdiri dari menggunakan BigInteger untuk operasi aritmatika atau ekspresi menggunakan metode pustaka yang menggunakan BigInteger. BigInteger tidak meluap. Ini akan menggunakan semua memori yang tersedia, jika perlu. Metode hitungnya biasanya hanya sedikit kurang efisien daripada operasi bilangan bulat. Mungkin saja hasil yang menggunakan BigInteger mungkin melampaui nilai maksimum atau minimum untuk bilangan bulat, namun, luapan tidak akan terjadi dalam aritmatika yang mengarah ke hasil. Pemrograman dan desain masih perlu menentukan apa yang harus dilakukan jika hasil BigInteger melampaui nilai maksimum atau minimum untuk tipe hasil primitif yang diinginkan, mis. Int atau panjang.
Program CERT Carnegie Mellon Software Engineering Institute dan Oracle telah menciptakan serangkaian standar untuk pemrograman Java yang aman. Termasuk dalam standar adalah teknik untuk mencegah dan mendeteksi integer overflow. Standar ini diterbitkan sebagai sumber daya online yang dapat diakses secara bebas di sini: The CERT Oracle Secure Coding Standard untuk Java
Bagian standar yang menggambarkan dan berisi contoh praktis teknik pengkodean untuk mencegah atau mendeteksi bilangan bulat bilangan bulat ada di sini: NUM00-J. Deteksi atau cegah integer overflow
Formulir buku dan formulir PDF dari CERT Oracle Secure Coding Standard untuk Java juga tersedia.
sumber
Baru saja mengalami masalah ini sendiri, inilah solusi saya (untuk perkalian dan penambahan):
jangan ragu untuk memperbaiki jika salah atau jika dapat disederhanakan. Saya telah melakukan beberapa pengujian dengan metode multiplikasi, sebagian besar kasus tepi, tetapi masih bisa salah.
sumber
int*int
, saya pikir cukup castinglong
dan melihat apakah hasilnya cocokint
akan menjadi pendekatan tercepat. Karenalong*long
, jika seseorang menormalkan operan menjadi positif, satu dapat membagi masing-masing menjadi bagian atas dan bawah 32-bit, mempromosikan setiap setengah hingga panjang (hati-hati tentang ekstensi tanda!), Dan kemudian menghitung dua produk parsial [salah satu bagian atas harus menjadi nol].Ada perpustakaan yang menyediakan operasi aritmatika yang aman, yang memeriksa bilangan bulat overflow / underflow. Misalnya, IntMath.checkedAdd Guava (int a, int b) mengembalikan jumlah
a
danb
, asalkan tidak meluap, dan melemparArithmeticException
jikaa + b
meluap dalamint
aritmatika yang ditandatangani .sumber
Math
kelas berisi kode yang sama.Itu membungkus.
misalnya:
cetakan
sumber
Saya pikir Anda harus menggunakan sesuatu seperti ini dan itu disebut Upcasting:
Anda dapat membaca lebih lanjut di sini: Mendeteksi atau mencegah integer overflow
Sumbernya cukup andal.
sumber
Itu tidak melakukan apa-apa - under / overflow terjadi begitu saja.
"-1" yang merupakan hasil perhitungan yang meluap tidak berbeda dengan "-1" yang dihasilkan dari informasi lainnya. Jadi, Anda tidak dapat memberi tahu melalui beberapa status atau hanya dengan memeriksa nilai apakah itu meluap.
Tetapi Anda bisa pintar tentang perhitungan Anda untuk menghindari luapan, jika itu penting, atau setidaknya tahu kapan itu akan terjadi. Apa situasimu
sumber
sumber
Saya pikir ini harus baik-baik saja.
sumber
Ada satu kasus, yang tidak disebutkan di atas:
akan menghasilkan:
Kasus ini dibahas di sini: Overflow integer menghasilkan Nol.
sumber