Java의 Long과 Double, 캐스팅

woo·2025년 4월 10일

잘 쓴 글도 좋지만, 덜 잘 쓴 글이라도 쓰지 않은 글보다는 낫기에 간단히 쓴다.
자바의 primitive type 중, long과 double 자료형은 비교적 생소하다.
사실 알고리즘 문제에서 long 범위까지 가는 경우는 은근 적고 double은 정말 기억도 안 난다.

1. 두 자료형 특징

  • long : 64비트의 메모리로 정수 값을 표현함
  • double : 64비트 메모리로 실수 값을 표현함

두 자료 간 형변환이 일어나는 경우도 지금까지 겪어 본 적은 없는 것 같다.
그것이 명시적 형변환이건, 묵시적 형변환이건 간에 말이다.

그러나, 만약 형변환이 발생하는 경우를 고려한다면 신중하게 코드를 작성해야 한다.
이러한 생각이 든 이유는 다음과 같은 코드를 맞닥뜨려 느낀 바가 있기 때문이다.

2. 형변환 시 오류 발생

다음 테스트 코드를 살펴보자

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;

public class LongToDoubleTest {
    @Test
    void longToDoubleTest(){
        // long은 64비트로 정수 값을 표현함
        long longValue = 111_111_111_111_111_111L;
        System.out.println("longValue = " + longValue);
        // double은 64비트로 실수 값을 표현함, 캐스팅 과정에서 유실 발생
        double doubleValue = longValue;
        System.out.println("doubleValue = " + doubleValue);
        System.out.println("doubleValue = " + new BigDecimal(doubleValue).toString());
        long longValue2 = (long)doubleValue;
        System.out.println("longValue2 = " + longValue2);

        Assertions.assertEquals(longValue, doubleValue);
        Assertions.assertEquals(doubleValue, longValue2);
        Assertions.assertEquals(longValue, longValue2);
    }
}

이 테스트 코드에서 에러가 발생하는 지점은 어디일까?
정답은 맨 마지막 라인이다.

3. JLS의 형변환 문서

Java Language Specific- JLS의 형변환 관련 문서가 있다
문서 보기(5.1.2)

5.1.2의 형변환 대목을 보면

19 specific conversions on primitive types are called the widening primitive conversions:

byte to short, int, long, float, or double

short to int, long, float, or double

char to int, long, float, or double

int to long, float, or double

long to float or double

float to double

A widening primitive conversion does not lose information about the overall magnitude of a numeric value in the following cases, where the numeric value is preserved exactly:

from an integral type to another integral type

from byte, short, or char to a floating-point type

from int to double

from float to double

A widening primitive conversion from int to float, or from long to float, or from long to double, may result in loss of precision, that is, the result may lose some of the least significant bits of the value. In this case, the resulting floating-point value will be a correctly rounded version of the integer value, using the round to nearest rounding policy (§15.4).

A widening conversion of a signed integer value to an integral type T simply sign-extends the two's-complement representation of the integer value to fill the wider format.

A widening conversion of a char to an integral type T zero-extends the representation of the char value to fill the wider format.

A widening conversion from int to float, or from long to float, or from int to double, or from long to double occurs as determined by the rules of IEEE 754 for converting from an integer format to a binary floating-point format.

A widening conversion from float to double occurs as determined by the rules of IEEE 754 for converting between binary floating-point formats.

Despite the fact that loss of precision may occur, a widening primitive conversion never results in a run-time exception (§11.1.1).

이 문서에 따라서 long의 float, double 중 하나로 형변환이 된다.
그리고 인텔리제이 통해서 해당 assertEquals의 구현부로 이동하면 expected와 actual 모두 double 형태이다.

따라서 Assertions.assertEquals(longValue, doubleValue) 코드에서 longValue가 double로 묵시적 형변환이 일어나는 것을 알 수 있다.

마찬가지 이유로 longValue2도 double로 형변환이 일어나기에 같은 이유로 테스트가 통과된다 짐작할 수 있다.

4. 데이터 유실

그러나, 마지막 테스트는 통과하지 못하는데 그 이유는 long끼리의 비교이기 때문이다.
longValue는 18자리의 아주 큰 수인데, 이 수를 doubleValue로 캐스팅하면서 정보의 손실이 발생한 것이다.

double은 64비트에 실수(소수점 부분 포함)를 나타내기 때문에 정수부에 표현할 수 있는 수의 범위가 그만큼 좁다. 반면 long은 64비트를 온전히 정수부에 할당하므로 double에 비해 정수부에서 표현 가능한 수의 범위가 넓다.

그리고 매우 큰 long을 double로 캐스팅 과정에서 정보 손살이 일어났고, 정보 손실로 변질된 dobuleValue를 다시 long 타입으로 캐스팅하여 longValue2에 저장했다.

그래서 변질이 일어난 longValue2와 longValue가 같지 않은 것이다.

마치며

그래서 금융이나 특정 데이터 등의 방대하고 정확성이 요구되는 데이터 타입에는 BigDecimal 타입의 자료를 고려해야 한다.

profile
BE, DE(지망생)

0개의 댓글