Bu kod:
System.out.println(Math.abs(Integer.MIN_VALUE));
İadeler -2147483648
Mutlak değeri olarak döndürmemeli 2147483648
mi?
Bu kod:
System.out.println(Math.abs(Integer.MIN_VALUE));
İadeler -2147483648
Mutlak değeri olarak döndürmemeli 2147483648
mi?
Yanıtlar:
Integer.MIN_VALUE
olduğu -2147483648
, ancak 32 bitlik tamsayı içerebilir yüksek değerdir +2147483647
. +2147483648
32 bitlik bir int içinde temsil etmeye çalışmak , etkin bir şekilde "dönecektir" -2147483648
. Bunun nedeni, işaretli tamsayılar kullanıldığında, ikisinin tamamlayıcı ikili gösterimlerinin +2147483648
ve -2147483648
aynı olmasıdır. Ancak, +2147483648
menzil dışında kabul edildiği için bu bir sorun değildir .
Bu konuyla ilgili biraz daha okumak için , İki'nin tamamlayıcısı hakkındaki Wikipedia makalesine göz atmak isteyebilirsiniz .
Gösterdiğiniz davranış gerçekten de sezgiseldir. Ancak, bu davranış javadocMath.abs(int)
tarafından şunlar için belirtilmiştir :
Argüman negatif değilse, argüman döndürülür. Bağımsız değişken negatifse, bağımsız değişkenin olumsuzluğu döndürülür.
Yani, Math.abs(int)
aşağıdaki Java kodu gibi davranmalıdır:
public static int abs(int x){
if (x >= 0) {
return x;
}
return -x;
}
Yani, olumsuz durumda -x
.
Göre JLS bölümüne 15.15.4 , -x
eşit olduğu (~x)+1
durumlarda, ~
bit seviyesinde tamamlayıcı bir operatördür.
Bunun kulağa doğru gelip gelmediğini kontrol etmek için, örnek olarak -1'i alalım.
Tamsayı değeri -1
, 0xFFFFFFFF
Java'da onaltılık olarak not edilebilir (bunu bir println
veya başka bir yöntemle kontrol edin ). -(-1)
Böylece almak :
-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
Yani işe yarıyor.
Şimdi deneyelim Integer.MIN_VALUE
. En düşük tamsayının 0x80000000
, yani ilk bitin 1'e ve kalan 31 bitin 0'a ayarlanmasıyla temsil edilebileceğini bilerek , elimizde:
-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1
= 0x80000000 = Integer.MIN_VALUE
Ve bu yüzden Math.abs(Integer.MIN_VALUE)
geri döner Integer.MIN_VALUE
. Ayrıca unutmayınız 0x7FFFFFFF
olduğunu Integer.MAX_VALUE
.
Bununla birlikte, gelecekte bu karşı-sezgisel geri dönüş değeri nedeniyle sorunlardan nasıl kaçınabiliriz?
Bunu yapabilirdik @Bombe tarafından sivri out gibi , bizim dökme int
için s long
önce. Ancak biz de
int
s'ye çevirin, bu işe yaramıyor çünkü
Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
.long
şekilde asla Math.abs(long)
eşit bir değerle çağırmayacağımızı umarak devam edin Long.MIN_VALUE
, çünkü bizde de var Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
.Her BigInteger
yerde s kullanabiliriz , çünkü BigInteger.abs()
gerçekten her zaman pozitif bir değer döndürür. Bu, ham tam sayı türlerini değiştirmekten biraz daha yavaş olsa da iyi bir alternatiftir.
Bunun için kendi paketleyicimizi yazabiliriz Math.abs(int)
:
/**
* 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
(esas olarak taşan Integer.MAX_VALUE
için 0
yerine Integer.MIN_VALUE
)Son bir not olarak, bu sorun bir süredir biliniyor gibi görünüyor. Örneğin , ilgili bulma hataları kuralı hakkındaki bu girişe bakın .
Beklediğiniz sonucu görmek için şunlara Integer.MIN_VALUE
gönderin long
:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
Math.abs
negatif bir sayı döndürerek mantığa aykırı olduğu gerçeğini çözmez :Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
ArithmeticException
? Ayrıca, davranış, API belgelerinde açıkça belgelenmiştir.
Math.abs(long)
. Buradaki hatam için özür dilerim: "Soruyu soranın Math.abs(long)
beklediği sonucu görmenin" basit bir yolu olarak gösterdiğiniz zaman, bir düzeltme olarak kullanımını önerdiğinizi anladım. Afedersiniz.
Fakat (int) 2147483648L == -2147483648
pozitif karşılığı olmayan bir negatif sayı vardır, dolayısıyla pozitif değeri yoktur. Long.MAX_VALUE ile aynı davranışı göreceksiniz.
Java 15'te bunun için bir düzeltme var, int ve long için bir yöntem olacak. Sınıflarda bulunacaklar
java.lang.Math and java.lang.StrictMath
Metodlar.
public static int absExact(int a)
public static long absExact(long a)
Eğer geçersen
Integer.MIN_VALUE
VEYA
Long.MIN_VALUE
Bir İstisna atılır.
https://bugs.openjdk.java.net/browse/JDK-8241805
Long.MIN_VALUE veya Integer.MIN_VALUE değerinin pozitif bir değer geçirilip geçirilmediğini görmek istiyorum, bir istisna değil, döndürür.