Math.abs, Tamsayı.Min_VALUE için yanlış değer döndürüyor


92

Bu kod:

System.out.println(Math.abs(Integer.MIN_VALUE));

İadeler -2147483648

Mutlak değeri olarak döndürmemeli 2147483648mi?

Yanıtlar:


103

Integer.MIN_VALUEolduğu -2147483648, ancak 32 bitlik tamsayı içerebilir yüksek değerdir +2147483647. +214748364832 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 +2147483648ve -2147483648aynı olmasıdır. Ancak, +2147483648menzil 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 .


6
Bir sorun değil, etkiyi küçümsemek, sorun anlamına gelebilir. Kişisel olarak, daha yüksek seviyeli bir dilde dinamik olarak büyüyen bir istisna veya sayı sistemi tercih ederim.
Maarten Bodewes

41

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 , -xeşit olduğu (~x)+1durumlarda, ~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, 0xFFFFFFFFJava'da onaltılık olarak not edilebilir (bunu bir printlnveya 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 0x7FFFFFFFolduğ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 intiçin s longönce. Ancak biz de

    • onları tekrar ints'ye çevirin, bu işe yaramıyor çünkü Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE).
    • Ya da bir 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 BigIntegeryerde 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);
}
  • Bir tam sayı bit usulü kullanarak ve sonuç negatif olmayan sağlamak, yüksek bit temizlemek için: int positive = value & Integer.MAX_VALUE(esas olarak taşan Integer.MAX_VALUEiçin 0yerine 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 .


12

Aşağıda Java doc içinde Math.abs () için şöyle diyor javadoc :

Bağımsız değişken, gösterilebilir en negatif int değeri olan Tamsayı.MIN_VALUE değerine eşitse, sonuç negatif olan aynı değerdir.


4

Beklediğiniz sonucu görmek için şunlara Integer.MIN_VALUEgönderin long:

System.out.println(Math.abs((long) Integer.MIN_VALUE));

1
Gerçekten de olası bir düzeltme! Ancak bu, Math.absnegatif bir sayı döndürerek mantığa aykırı olduğu gerçeğini çözmez :Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
bernard paulus

1
@bernardpaulus, peki, atmanın yanı sıra ne yapması gerekiyor ArithmeticException? Ayrıca, davranış, API belgelerinde açıkça belgelenmiştir.
Bombe

Sorunuzun iyi bir cevabı yok ... Sadece bir hata kaynağı olan bu davranışın kullanımıyla sabitlenmediğini belirtmek istedim 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.
bernard paulus

Java 15'te yeni yöntemlerle aslında bir İstisna atılır.
chiperortiz

1

2147483648, java'da bir tamsayıda depolanamaz, ikili gösterimi -2147483648 ile aynıdır.


0

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.


0

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.


-1

Math.abs büyük sayılarla her zaman çalışmaz 7 yaşında öğrendiğim bu küçük kod mantığını kullanıyorum!

if(Num < 0){
  Num = -(Num);
} 

sBurada ne var?
aioobe

Özür dilerim, bunu orijinal
Dave

Öyleyse, Numpasajdan Integer.MIN_VALUEönce eşitse bu ne ile sonuçlanır ?
aioobe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.