long 타입에서 int 타입으로 형변환하는 과정에 대해 알아보자.
long val1Long = 108095103513L;
long val2Long = 108030648857L;
int val1Int = (int) 108095103513L;
int val2Int = (int) 108030648857L;
boolean isEqual = (val1Int - val2Int) == (val1Long - val2Long);
위에서 isEqual
는 true 이다.
int 로 cast 되면서 정보 손실이 있었지만 그래도 true이다. 왜일까?
val1Int, val2Int 는 int 로 cast 되면서 정보 손실이 일어난다.
여기서 말하는 정보 손실이란 int 값에 들어갈 수 있는 4 bytes (32 bits) 에108095103513L
라는 long 타입 8 bytes (32 bits) 가 들어갈 수 없기 때문에 형변환 때 32 bit 단위로 짤려지는 것을 말한다.
아래 long 값을 살펴보자.
long x = 2147483648L; // value is 2^31
// x = 00000000000000000000000000000000 10000000000000000000000000000000 (in binary)
long x 는 2^31 이다. 즉, 위 변수 x
는 int 에서 담을 수 있는 최대 크기 (2^31 - 1) 을 넘는다.
이를, int 로 형변환 하면 binary 는 어떻게 변환되는 것일까?
JVM 에서 long 타입을 int 로 변환할 때는 다음과 같은 규칙을 따른다.
- 최상위 (가장 왼쪽) 32 bits 를 날린다.
- 최하위 (가장 오른쪽) 32 bits 를 남긴다.
이 규칙을 적용 해보면 x
의 binary 에서 가장 왼쪽 32 bits 00000000000000000000000000000000
가 날라간다.
오른쪽 32 bits 는 남겼으므로 결과는 10000000000000000000000000000000
가 된다.
JVM 에서 숫자의 크기를 표현할때, int 와 long 의 경우 2의 보수 표현을 사용한다.
따라서, long x = 2147483648L
가 int 로 cast 되면 아래와 같은 결과를 가진다.
int y = (int) x;
// y = 10000000000000000000000000000000 (in binary) -> -(2^32) -> -2147483648
참고로 2의 보수로 표현하는 방법은 아래와 같다.
- 가장 왼쪽 bit 가 1 이라면, 음수이다.
- 모든 비트를 invert 한다. (0들을 1로, 1들을 0으로)
- 결과에 1을 더한다
- 결과의 부정한다. (-라면 +로, +라면 -로)
10000000000000000000000000000000
을 2의 보수로 나타내면 아래처럼 생각해보면 된다.
- 가장 왼쪽 bit 가 1 이니까, 음수이구나.
- bit 를 전부 invert 하면
01111111111111111111111111111111
- 거기에 1을 더하면?
1000000000000000000000000000000
- 결과를 부정하면
-2147483648
결과 값을 가진다.
처음 예제로 돌아가자.
이제 isEqual
이 왜 true 인지 근본적으로 이해할 수 있다.
long val1Long = 108095103513L;
long val2Long = 108030648857L;
int val1Int = (int) 108095103513L;
int val2Int = (int) 108030648857L;
boolean isEqual = (val1Int - val2Int) == (val1Long - val2Long);
두 정수(long)는 결국엔 똑같은 규칙(JVM의 형변환)을 가지고 int 로 형변환을 하게 되어있다.
형변환하는 방법을 따라서 (2^31), (2^31 + 1), (2^32 + 2), ... 를 모두 int 로 계산해보면 int 로 변환되더라도 1씩 증가하는 일련의 규칙이 있는 것을 알 수 있다.
따라서, 두 값의 차이는 같을 수 밖에 없다.
'기술과 생각 > 자바와 JVM' 카테고리의 다른 글
자바 strict weak ordering 과 Comparison method violates its general contract 에러 (0) | 2023.02.08 |
---|---|
왜 관습적으로 private static final 을 사용하는 것일까? (4) | 2022.11.10 |
VO(Value Object) 이해하기 (0) | 2021.12.15 |