PYTHON

파이썬 연산자 — 나눗셈이 셋인 이유(/, //, %)

PANCO_IT
#파이썬#python#연산자#나눗셈##나머지#거듭제곱#파이썬기초#파이썬독학

구구단표가 인쇄된 계산기

이미지 출처: Pexels, Fatih Yurtman · Pexels License

같은 7과 2를 두고 세 줄을 나란히 돌려 보겠습니다. 결과를 먼저 보고, 설명은 그다음입니다.

한 문제, 세 가지 답
print(7 / 2)
print(7 // 2)
print(7 % 2)

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

3.5, 3, 1. 세 줄 모두 "7을 2로 나눈다"인데 답이 다릅니다. 파이썬이 이상한 게 아니라, 나눗셈이라는 말 안에 서로 다른 질문 세 개가 들어 있기 때문입니다. 피자 7조각을 두 사람이 나눈다고 해 보겠습니다. "한 사람이 얼마나 먹나"(3.5조각), "온전한 조각으로는 몇 개씩 주나"(3조각), "그러고 몇 조각이 남나"(1조각). 질문마다 연산자가 하나씩 배정돼 있습니다.

7 나누기 2를 /, //, %로 계산한 세 결과 3.5, 3, 1을 피자 조각 비유와 함께 세 칸으로 비교한 그림

세 연산자는 경쟁 관계가 아니라 분업 관계입니다. 몫(//)과 나머지(%)를 합치면 원래 수로 돌아옵니다: 7 = 2 × 3 + 1.

오늘은 이 셋을 세 라운드로 붙여 봅니다. 라운드마다 "무엇을 돌려주나", "어디서 헷갈리나", "실제로 언제 쓰나"를 봅니다. 마지막에 판정표로 정리합니다.

라운드 1 — 슬래시 하나(/)는 항상 실수를 돌려줍니다

/는 우리가 아는 나눗셈입니다. 다만 파이썬에는 규칙이 하나 더 있습니다. 나누어떨어지든 아니든 결과는 언제나 실수(float) 라는 것입니다.

나누어떨어져도 실수
print(8 / 2)          # 4가 아니라 4.0
print(type(8 / 2))    # <class 'float'>
print(10 / 4)         # 2.5
print(1 / 3)          # 0.3333333333333333

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

8 / 24가 아니라 4.0으로 찍힙니다. 결과가 정수여도 실수 상자에 담아 돌려준다는 뜻입니다. 지난 편에서 int("20")처럼 자료형을 따졌던 이유가 여기서 다시 나옵니다. 4.0은 화면에서 4처럼 보여도 정수가 아니라서, 뒤에서 "정수만 받는" 자리에 넣으면 막힙니다. 예를 들어 리스트의 몇 번째 칸을 고를 때 [8 / 2]를 쓰면 TypeError가 납니다. 그 이야기는 리스트 편에서 다시 만납니다.

마지막 줄 1 / 30.3333333333333333으로 끝나는 것도 눈여겨보세요. 컴퓨터는 소수를 딱 떨어지게 저장하지 못해서 어딘가에서 잘라야 합니다. 그래서 0.1 + 0.2를 찍으면 0.30000000000000004가 나옵니다. 파이썬의 버그가 아니라 모든 언어가 같은 방식으로 저장하기 때문이고, 돈 계산처럼 정확해야 할 때 쓰는 도구는 따로 있습니다. 지금은 "실수 나눗셈 결과는 근사값일 수 있다" 정도로 기억해 두면 됩니다.

라운드 2 — 슬래시 둘(//)은 몫, 그런데 "자르기"가 아니라 "내림"입니다

//는 나눈 뒤 소수점 아래를 버리고 정수만 돌려줍니다. 결과도 정수(int)입니다.

몫만 남기기
print(7 // 2)         # 3
print(8 // 2)         # 4 (4.0이 아님)
print(type(7 // 2))   # <class 'int'>
print(7.0 // 2)       # 3.0 — 실수가 하나라도 섞이면 결과도 실수

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

여기까지는 "소수점을 자른다"로 이해해도 답이 맞습니다. 그런데 음수가 들어오면 이 이해가 틀립니다. 여기서 다들 한 번 넘어집니다.

음수에서는 답이 달라집니다
print(-7 / 2)         # -3.5
print(-7 // 2)        # -4  (-3이 아닙니다)
print(int(-7 / 2))    # -3  (int()는 0 쪽으로 자름)

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

-7 // 2-3이 아니라 -4입니다. //는 소수점을 "자르는" 것이 아니라 수직선에서 왼쪽(작은 쪽) 정수로 내리는 연산이기 때문입니다. -3.5의 왼쪽 정수는 -4입니다. 반대로 int(-3.5)는 소수점을 잘라 -3이 됩니다. 양수에서는 두 방법의 답이 같아서 차이를 못 느끼다가, 음수에서 처음 갈라집니다.

-7 나누기 2 = -3.5를 수직선에 놓고, //는 왼쪽 정수 -4로 내리고 int()는 0 쪽으로 잘라 -3이 되는 차이를 표시한 그림

//는 "내림(floor)"입니다. 수직선에서 언제나 왼쪽으로 갑니다. 그래서 이름도 floor division입니다.

이 성질은 %와 짝이 맞도록 일부러 그렇게 정한 것입니다. 라운드 3에서 이유가 나옵니다.

라운드 3 — 퍼센트(%)는 나머지, 부호는 나누는 수를 따릅니다

%는 나누고 남은 것을 돌려줍니다. 백분율과는 상관이 없습니다. 이름이 그렇게 생겼을 뿐입니다.

나머지 구하기
print(7 % 2)          # 1
print(8 % 2)          # 0 — 나누어떨어지면 0
print(10 % 3)         # 1
print(2 % 5)          # 2 — 작은 수를 큰 수로 나누면 자기 자신이 남습니다

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

%가 자주 쓰이는 곳은 딱 두 가지입니다. 하나는 짝수·홀수 판별입니다. n % 20이면 짝수, 1이면 홀수입니다. 다른 하나는 주기적으로 돌아오는 것을 셀 때입니다. 요일, 시계, "3명씩 조를 짜면 몇 명이 남나" 같은 계산입니다.

그리고 //%는 항상 짝으로 움직입니다. 어떤 수 ab로 나눴을 때, a == b * (a // b) + a % b 가 언제나 성립하도록 파이썬이 두 연산자를 맞춰 두었습니다. 7 = 2 × 3 + 1. 음수도 확인해 보겠습니다.

몫과 나머지는 짝 — 음수에서도 성립
a, b = -7, 2
print(a // b, a % b)            # -4 1
print(b * (a // b) + a % b)     # -7 — 원래 수로 돌아옵니다
print(7 % -2)                   # -1 — 나머지 부호는 나누는 수(b)를 따릅니다
print(divmod(7, 2))             # (3, 1) — 몫과 나머지를 한 번에

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

-7 // 2-4로 내렸기 때문에 -7 % 21이 됩니다. 만약 //-3으로 잘랐다면 나머지는 -1이 되어야 했을 겁니다. 파이썬은 나머지의 부호가 나누는 수와 같아지는 쪽을 골랐고, 그래서 7 % -2-1입니다. 다른 언어(C, 자바)는 반대로 정해 두어서 -7 % 2-1입니다. 여러 언어를 오갈 때 가장 자주 틀리는 자리입니다. 마지막 줄의 divmod()는 몫과 나머지를 한 번에 돌려주는 도구입니다.

판정표 — 세 연산자, 언제 무엇을 쓰나

연산자이름7 ? 2결과 자료형이럴 때 씁니다
/나눗셈3.5항상 float평균, 비율, "한 명당 얼마"
//몫(내림)3int (실수 섞이면 float)"몇 개씩", "몇 번 들어가나", 자리 계산
%나머지1int (실수 섞이면 float)짝홀 판별, 주기, "몇 개 남나"

세 문장으로 줄이면 이렇습니다. /는 정확한 값(실수), //는 온전한 개수(내림), %는 남는 양. 그리고 //%a == b * (a // b) + a % b로 묶여 있어서 음수에서도 서로 어긋나지 않습니다.

덤 — 나머지 연산자들과 순서

나눗셈 셋 외의 산술 연산자는 우리가 아는 그대로입니다. +, -, *, 그리고 거듭제곱 **입니다. 2 ** 10은 1024입니다. ^가 아니라는 점만 주의하면 됩니다. 파이썬에서 ^는 전혀 다른 연산(비트 연산)이라 2 ^ 108이 나옵니다.

우선순위 — 곱셈·나눗셈이 덧셈·뺄셈보다 먼저
print(2 + 3 * 4)        # 14 (3 * 4 먼저)
print((2 + 3) * 4)      # 20 (괄호 먼저)
print(2 ** 3 ** 2)      # 512 (거듭제곱은 오른쪽부터: 3 ** 2 = 9, 2 ** 9)
print(-3 ** 2)          # -9 (거듭제곱이 부호보다 먼저: -(3 ** 2))
print(7 // 2 * 2)       # 6 (//와 *는 같은 급, 왼쪽부터)

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

수학 시간의 순서와 같습니다. 괄호 → 거듭제곱 → 곱셈·나눗셈(*, /, //, %) → 덧셈·뺄셈. 다만 두 줄은 자주 틀립니다. 2 ** 3 ** 2는 오른쪽부터 계산해 512이고, -3 ** 2-9입니다. (-3) ** 2를 원하면 괄호를 써야 합니다. 헷갈리면 괄호를 치는 것이 정답입니다. 괄호는 공짜이고, 읽는 사람도 편합니다.

한 가지 더. 0으로 나누면 세 연산자 모두 ZeroDivisionError로 멈춥니다. 7 / 0, 7 // 0, 7 % 0 전부입니다. 입력값으로 나눌 때는 0이 들어올 수 있다는 것을 기억해 두어야 하는데, 그것을 막는 방법(if)이 바로 다음 편입니다.

실전 — 초를 분과 초로 바꾸기

//%가 함께 일하는 가장 흔한 장면입니다. 125초는 몇 분 몇 초일까요.

125초 → 2분 5초
total = 125
minutes = total // 60      # 60이 몇 번 들어가나 → 2
seconds = total % 60       # 그러고 몇 초 남나 → 5
print(f"{total}초는 {minutes}분 {seconds}초입니다.")

# 응용: 3,700초를 시·분·초로
t = 3700
print(t // 3600, "시간", t % 3600 // 60, "분", t % 60, "초")

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

첫 줄은 2분 5초, 둘째 줄은 1 시간 1 분 40 초가 나옵니다. t % 3600 // 60은 "3600으로 나눈 나머지(100)를 다시 60으로 나눈 몫(1)"입니다. %//가 같은 급이라 왼쪽부터 계산됩니다. total59, 60, 61로 바꿔 돌려 보면 경계에서 어떻게 넘어가는지 보입니다.

셋 중 하나는 오늘 본문에 없는 함정입니다

미니 퀴즈

OX 3문제 + 4지선다 3문제


포켓코딩(PYTHON1) 5편 · 4편(파이썬 입력과 형변환)에서 이어집니다. 예제 코드는 강사 개인 GitHub 저장소의 연습 문제를 바탕으로 다시 썼습니다. 다음 편: 파이썬 조건문 if — 0으로 나누기 전에 멈추는 법.

PANCO_IT

IT 지식을 정리하는 개발자

관련 글