[java] Math.abs가 Integer.Min_VALUE에 대해 잘못된 값을 반환합니다.

이 코드 :

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

보고 -2147483648

절대 값을 다음과 같이 반환하지 않아야 2147483648합니까?



답변

Integer.MIN_VALUE입니다 -2147483648만, 32 비트 정수가 포함 할 수있는 가장 높은 값이다 +2147483647. +214748364832 비트 int 로 표현하려고 하면 효과적으로 “롤오버”됩니다 -2147483648. 부호있는 정수를 사용하는 경우, 2 개의이 바이너리 표현을 보완하기 때문이다 +2147483648-2147483648동일하다. 그러나 +2147483648범위를 벗어난 것으로 간주 되므로 문제가되지 않습니다 .

이 문제에 대해 조금 더 읽으려면 Two’scomplement에 대한 Wikipedia 기사 를 확인하십시오 .


답변

당신이 지적하는 행동은 실제로 반 직관적입니다. 그러나이 동작은 다음에 대해 javadoc에서Math.abs(int) 지정한 동작입니다 .

인수가 음수가 아니면 인수가 반환됩니다. 인수가 음수이면 인수의 부정이 반환됩니다.

즉, Math.abs(int)다음 Java 코드와 같이 작동해야합니다.

public static int abs(int x){
    if (x >= 0) {
        return x;
    }
    return -x;
}

즉, 부정적인 경우 -x.

받는 따르면 JLS 부 15.15.4 의은 -x과 동일하다 (~x)+1, 여기서 ~비트 보수 연산자이다.

이것이 올바른지 확인하기 위해 -1을 예로 들어 봅시다.

정수 값 -10xFFFFFFFFJava에서 16 진수 로 표시 할 수 있습니다 ( println또는 다른 방법으로 확인). -(-1)따라서 복용하면 다음 이 제공됩니다.

-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1

그래서 작동합니다.

이제 Integer.MIN_VALUE. 가장 낮은 정수는로 나타낼 수 있습니다 0x80000000. 즉, 첫 번째 비트는 1로 설정되고 나머지 31 비트는 0으로 설정됩니다.

-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1
                     = 0x80000000 = Integer.MIN_VALUE

그리고 이것이 Math.abs(Integer.MIN_VALUE)반환하는 이유 Integer.MIN_VALUE입니다. 또한 0x7FFFFFFF입니다 Integer.MAX_VALUE.

즉, 미래에 이러한 반 직관적 인 반환 값으로 인한 문제를 어떻게 피할 수 있습니까?

  • 우리는 수, @Bombe에 의해 지적 밖으로로서 , 우리의 캐스팅 int에들 long전에. 그러나 우리는

    • ints
      로 다시 캐스팅하면 Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE).
    • 또는 계속 long의 어떻게 든 우리가 호출하지 않을거야 바라고 Math.abs(long)값과 같 Long.MIN_VALUE우리는 또한 가지고 있기 때문에 Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE.
  • BigIntegers BigInteger.abs()는 실제로 항상 양의 값을 반환 하기 때문에 모든 곳에서 사용할 수 있습니다 . 이것은 원시 정수 유형을 조작하는 것보다 약간 느리지 만 좋은 대안입니다.

  • 다음 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);
}
  • 정수 비트 AND를 사용하여 상위 비트를 지우고 결과가 음수가 아닌지 확인합니다. int positive = value & Integer.MAX_VALUE(본질적 Integer.MAX_VALUE으로 0대신 에서 ~ 까지 오버플 로 됨 Integer.MIN_VALUE)

마지막으로이 문제는 한동안 알려진 것으로 보입니다. 예를 들어 해당 findbugs 규칙에 대한이 항목을 참조하십시오 .


답변

다음은 javadoc의 Math.abs ()에 대한 Java 문서의 내용입니다 .

인수가 가장 음의 표현 가능한 int 값인 Integer.MIN_VALUE의 값과 같으면 결과는 동일한 값인 음수입니다.


답변

예상 한 결과를 보려면 다음으로 캐스트 Integer.MIN_VALUE하십시오 long.

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


답변

2147483648은 Java의 정수로 저장할 수 없으며 이진 표현은 -2147483648과 동일합니다.


답변

그러나 (int) 2147483648L == -2147483648 양수 등가물이없는 음수가 하나 있으므로 양수 값이 없습니다. Long.MAX_VALUE에서도 동일한 동작을 볼 수 있습니다.


답변

이것에 대한 수정이 Java 15에서 int 및 long 메서드가 될 것입니다. 그들은 수업에 참석합니다

java.lang.Math and java.lang.StrictMath

방법.

public static int absExact(int a)
public static long absExact(long a)

통과하면

Integer.MIN_VALUE

또는

Long.MIN_VALUE

예외가 발생합니다.

https://bugs.openjdk.java.net/browse/JDK-8241805

Long.MIN_VALUE 또는 Integer.MIN_VALUE가 전달되는지 확인하고 싶습니다. 양수 값은 예외가 아니라 반환됩니다.