Kode ini:
System.out.println(Math.abs(Integer.MIN_VALUE));
Kembali -2147483648
Haruskah itu tidak mengembalikan nilai absolut sebagai 2147483648
?
Kode ini:
System.out.println(Math.abs(Integer.MIN_VALUE));
Kembali -2147483648
Haruskah itu tidak mengembalikan nilai absolut sebagai 2147483648
?
Jawaban:
Integer.MIN_VALUE
adalah -2147483648
, tetapi nilai tertinggi yang dapat dimiliki oleh integer 32 bit adalah +2147483647
. Mencoba untuk mewakili +2147483648
dalam int 32 bit akan secara efektif "berguling" ke -2147483648
. Ini karena, saat menggunakan bilangan bulat bertanda, kedua representasi biner pelengkap dari +2147483648
dan -2147483648
adalah identik. Ini bukan masalah, bagaimanapun, karena +2147483648
dianggap di luar jangkauan.
Untuk sedikit lebih banyak membaca tentang masalah ini, Anda mungkin ingin melihat artikel Wikipedia tentang pelengkap Two .
Perilaku yang Anda tunjukkan memang kontra-intuitif. Namun, perilaku ini adalah perilaku yang ditentukan oleh javadoc untukMath.abs(int)
:
Jika argumen tidak negatif, argumen dikembalikan. Jika argumennya negatif, negasi argumen dikembalikan.
Artinya, Math.abs(int)
harus berperilaku seperti kode Java berikut:
public static int abs(int x){
if (x >= 0) {
return x;
}
return -x;
}
Artinya, dalam kasus negatif -x
,.
Menurut JLS bagian 15.15.4 , -x
adalah sama dengan (~x)+1
, di mana ~
operator pelengkap bitwise.
Untuk memeriksa apakah ini terdengar benar, mari kita ambil -1 sebagai contoh.
Nilai integer -1
dapat dicatat seperti 0xFFFFFFFF
dalam heksadesimal di Java (lihat ini dengan a println
atau metode lain). Mengambil -(-1)
demikian memberi:
-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
Jadi, itu berhasil.
Mari kita coba sekarang dengan Integer.MIN_VALUE
. Mengetahui bahwa bilangan bulat terendah dapat diwakili oleh 0x80000000
, yaitu, bit pertama disetel ke 1 dan 31 bit tersisa disetel ke 0, kami memiliki:
-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1
= 0x80000000 = Integer.MIN_VALUE
Dan inilah mengapa Math.abs(Integer.MIN_VALUE)
kembali Integer.MIN_VALUE
. Juga mencatat bahwa 0x7FFFFFFF
adalah Integer.MAX_VALUE
.
Karena itu, bagaimana kita dapat menghindari masalah karena nilai pengembalian kontra-intuitif ini di masa mendatang?
Kita bisa, seperti yang keluar menunjuk oleh @Bombe , cor kami int
s untuk long
sebelumnya. Kami, bagaimanapun, harus baik
int
s, yang tidak berfungsi karena
Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
.long
berharap bahwa kita tidak akan pernah menelepon Math.abs(long)
dengan nilai yang sama Long.MIN_VALUE
, karena kita juga punya Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
.Kita bisa menggunakan BigInteger
s dimana saja, karena BigInteger.abs()
memang selalu mengembalikan nilai positif. Ini adalah alternatif yang baik, meskipun sedikit lebih lambat daripada memanipulasi tipe integer mentah.
Kita dapat menulis pembungkus kita sendiri untuk Math.abs(int)
, seperti ini:
/**
* Fail-fast wrapper for {@link Math#abs(int)}
* @param x
* @return the absolute value of x
* @throws ArithmeticException when a negative value would have been returned by {@link Math#abs(int)}
*/
public static int abs(int x) throws ArithmeticException {
if (x == Integer.MIN_VALUE) {
// fail instead of returning Integer.MAX_VALUE
// to prevent the occurrence of incorrect results in later computations
throw new ArithmeticException("Math.abs(Integer.MIN_VALUE)");
}
return Math.abs(x);
}
int positive = value & Integer.MAX_VALUE
(pada dasarnya meluap dari Integer.MAX_VALUE
ke 0
alih-alih Integer.MIN_VALUE
)Sebagai catatan terakhir, masalah ini sepertinya sudah diketahui beberapa waktu lalu. Lihat misalnya entri ini tentang aturan findbugs yang sesuai .
Untuk melihat hasil yang Anda harapkan, transmisikan Integer.MIN_VALUE
ke long
:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
Math.abs
berlawanan dengan intuisi dengan mengembalikan angka negatif:Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
ArithmeticException
? Selain itu, perilaku tersebut didokumentasikan dengan jelas dalam dokumentasi API.
Math.abs(long)
. Saya minta maaf atas kesalahan saya di sini: Saya mengira Anda mengusulkan penggunaan Math.abs(long)
sebagai perbaikan, ketika Anda menunjukkannya sebagai cara sederhana untuk "melihat hasil yang diharapkan penanya". Maaf.
Tetapi (int) 2147483648L == -2147483648
ada satu bilangan negatif yang tidak memiliki padanan positif sehingga tidak ada nilai positif untuk itu. Anda akan melihat perilaku yang sama dengan Long.MAX_VALUE.
Ada perbaikan ini di Jawa 15 akan menjadi metode int dan panjang. Mereka akan hadir di kelas
java.lang.Math and java.lang.StrictMath
Metodenya.
public static int absExact(int a)
public static long absExact(long a)
Jika Anda lulus
Integer.MIN_VALUE
ATAU
Long.MIN_VALUE
Pengecualian dilemparkan.
https://bugs.openjdk.java.net/browse/JDK-8241805
Saya ingin melihat apakah Long.MIN_VALUE atau Integer.MIN_VALUE dilewatkan, nilai positif akan dikembalikan dan bukan pengecualian tetapi.
Math.abs tidak bekerja sepanjang waktu dengan angka-angka besar. Saya menggunakan logika kode kecil ini yang saya pelajari ketika saya berusia 7 tahun!
if(Num < 0){
Num = -(Num);
}
s
disini?
Num
sama dengan Integer.MIN_VALUE
sebelum cuplikan?