이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.
공부 › Java › 기본 문법
연산자와 형변환
7 / 2는 왜 3.5가 아니라 3이 나오나
자바에서 계산 결과는 숫자만이 아니라 타입으로도 정해집니다. int끼리 계산하면 결과도 int라서 소수점 아래나 범위를 넘는 부분이 사라지고, 타입이 섞이면 좁은 쪽을 넓은 쪽으로 자동으로 맞춘 뒤 계산합니다. +는 한쪽이 문자열이면 덧셈이 아니라 이어 붙이기가 됩니다.
실행으로 대조 · JDK 21에서 열여덟 가지 코드를 실행해 대조 (컴파일 오류 한 가지 포함)
이 실습에 나오는 개념
- 형변환
- 값의 타입을 다른 타입으로 바꾸는 것이다. 좁은 타입에서 넓은 타입으로 바꾸는 것(int에서 long, int에서 double)은 자동으로 일어나며, 타입이 섞인 계산에서도 좁은 쪽이 넓은 쪽으로 맞춰진 뒤 계산된다. 다만 자동으로 일어나는 변환 가운데 int에서 float, long에서 float, long에서 double은 큰 값의 아래쪽 자릿수가 달라질 수 있다. 넓은 타입에서 좁은 타입으로 바꾸는 것은 값을 잃을 수 있어서 (int)처럼 괄호 안에 타입을 직접 적어야 하며, 이것을 강제 형변환 또는 캐스팅이라고 한다. double을 int로 바꾸면 소수점 아래가 버려지고, 큰 정수를 더 좁은 정수 타입으로 바꾸면 위쪽 비트가 잘려 전혀 다른 값이 될 수 있다. 변수에 넣을 때 일어나는 형변환은 계산이 끝난 값에 적용되므로, 계산 도중에 이미 버려지거나 넘친 부분을 되살리지 못한다.
- 정수 나눗셈과 나머지
- 정수끼리 /로 나누면 결과도 정수이며 소수점 아래는 반올림 없이 버려진다. 버림은 0에 가까운 쪽으로 일어나서 7 / 2는 3이고 -7 / 2는 -3이다. % 연산자는 나눗셈의 나머지를 구하며, 나머지의 부호는 나누어지는 수(왼쪽)의 부호를 따른다. 정수를 0으로 나누거나 0으로 나머지를 구하면 ArithmeticException이 발생한다. 소수점까지 구하려면 나누기 전에 한쪽을 double로 바꾼다.
- 정수 오버플로
- 계산 결과가 그 타입이 담을 수 있는 범위를 넘어 엉뚱한 값이 되는 현상이다. 자바의 int는 -2147483648부터 2147483647까지 담을 수 있고, 이 범위를 넘으면 예외 없이 반대쪽 끝으로 넘어가 이어진다. 예를 들어 2147483647에 1을 더하면 -2147483648이 된다. 오류가 나지 않고 틀린 값이 조용히 쓰이므로 찾기 어렵다. 범위를 넘을 수 있는 계산에는 long을 쓰거나, 넘칠 때 예외를 던지는 Math.addExact 같은 메서드를 쓴다.
- 문자열 연결
- + 연산자의 한쪽이라도 문자열(String)이면 덧셈을 하지 않고 두 값을 이어 붙인 새 문자열을 만드는 것이다. 문자열이 아닌 쪽은 먼저 문자열로 바뀐 뒤 붙는다. +가 여러 개면 왼쪽부터 차례로 계산하므로, 1 + 2 + "3"은 3과 "3"을 붙인 "33"이 되고 "1" + 2 + 3은 "123"이 된다. 숫자끼리 먼저 더하려면 그 부분을 괄호로 묶는다.
이렇게 바꿔 보세요
- 처음 설정으로 — int끼리 나누면 3.5가 아니라 3이 나옵니다.
- double 변수에 받아도 — 계산은 이미 int로 끝나서 3.0이 됩니다.
- 한쪽을 double로 쓰면 — 계산 전에 타입이 맞춰져 3.5가 나옵니다.
- int의 범위를 넘으면 — 가장 큰 값에 1을 더하면 가장 작은 값이 됩니다.
- long 변수에 받기만 하면 — 넘친 뒤의 값이 그대로 들어갑니다.
- byte로 강제 형변환 — 200이 -56이 됩니다.
- 숫자와 문자열을 더하면 — 1 + 2 + "3"은 "6"도 "123"도 아닙니다.
처음 설정으로 실행하면
- 7 / 2: 양쪽이 모두 int이므로 결과도 int입니다. 수학에서의 값은 3.5입니다. 하지만 int는 소수점 아래를 담지 못하므로, 반올림 없이 버리고 3만 남깁니다.
- int r = 3: 계산된 값을 int 변수 r에 넣습니다. 타입이 같으므로 값은 그대로입니다.
- System.out.println(r): r에 든 값을 출력합니다. 출력은 3입니다.
실행 결과
- int끼리 나누면 결과도 int입니다. 소수점 아래는 반올림 없이 버려집니다.
- 소수점까지 얻으려면 나누기 전에 한쪽을 double로 만들어야 합니다.
코드
int r = 7 / 2; // int끼리 나눗셈
System.out.println(r);
이 모형의 전제
열여덟 가지 코드의 결과(오른쪽 식의 타입, 변수의 타입, 출력이나 예외)를 이 PC의 JDK 21에서 같은 코드를 실행한 결과와 대조했습니다. int r = 3.99가 컴파일 오류라는 것과 그 메시지도 같은 JDK의 컴파일러에 실제로 넣어 확인했습니다(메시지는 영어판 기준). 요약에 적은 7 / 0.0, (double) sum / count, "합: " + 1 + 2, n % 2 == 1, Math.round, Math.addExact의 동작도 함께 실행해 확인했습니다. 다루지 않은 것이 있습니다. float와 char와 short의 계산, byte b = 100처럼 범위 안의 정수 상수를 좁은 타입에 바로 넣을 수 있는 규칙, long을 double로 바꿀 때 큰 수에서 생기는 오차, 0.1 + 0.2 같은 실수 계산의 오차는 넣지 않았습니다. 7 / 2처럼 숫자만으로 된 식은 실제로는 컴파일러가 미리 계산해 두지만 결과는 같습니다.
앞 실습: 변수와 타입 · 다음 실습: 조건문과 반복문