산술 연산자
산술 연산자 (C# 참조)
숫자 타입의 피연산자와 함께 산술 연산을 수행하는 연산자들을 한데 모아 보면 이래요.
- 단항 연산자:
++(증가),--(감소),+(플러스),-(마이너스) - 이항 연산자:
*(곱셈),/(나눗셈),%(나머지),+(덧셈),-(뺄셈)
모든 정수 타입과 부동 소수점 타입이 이 연산자들을 지원해요.
int, uint, long, ulong 타입은 이 연산자들을 모두 정의해요. 나머지 정수 타입(sbyte, byte, short, ushort, char)은 ++와 -- 연산자만 정의하고요. 이 타입들을 피연산자로 쓰는 다른 연산은 값이 int 타입으로 변환되고 결과 타입도 int가 돼요. 피연산자들이 서로 다른 정수·부동 소수점 타입이면, 그런 타입이 존재할 때 값이 가장 근접하게 포함하는 타입으로 변환돼요. 자세한 내용은 C# 언어 사양의 숫자 승격 절에서 확인할 수 있어요. ++와 -- 연산자는 모든 정수·부동 소수점 숫자 타입과 char 타입에 대해 정의돼요. 복합 할당 식의 결과 타입은 왼쪽 피연산자의 타입이에요.
[!NOTE] 이 문서는 C#의 초기 버전부터 적용되는 내용을 다뤄요.
본문
++ 증가 연산자
단항 증가 연산자 ++는 피연산자를 1만큼 늘려요. 피연산자는 변수, 속성 접근, 인덱서 접근이어야 해요.
증가 연산자는 두 가지 형태로 지원돼요. 후위 증가 연산자 x++와 전위 증가 연산자 ++x죠.
후위 증가 연산자
x++의 결과는 연산 이전의 x 값이에요. 다음 예제를 보면 바로 확인할 수 있어요.
// 예제: snippets/shared/ArithmeticOperators.cs — PostfixIncrement
전위 증가 연산자
++x의 결과는 연산 이후의 x 값이에요.
// 예제: snippets/shared/ArithmeticOperators.cs — PrefixIncrement
-- 감소 연산자
단항 감소 연산자 --는 피연산자를 1만큼 줄여요. 피연산자는 변수, 속성 접근, 인덱서 접근이어야 해요.
감소 연산자도 두 가지 형태로 제공돼요. 후위 감소 연산자 x--와 전위 감소 연산자 --x예요.
후위 감소 연산자
x--의 결과는 연산 이전의 x 값이에요.
// 예제: snippets/shared/ArithmeticOperators.cs — PostfixDecrement
전위 감소 연산자
--x의 결과는 연산 이후의 x 값이에요.
// 예제: snippets/shared/ArithmeticOperators.cs — PrefixDecrement
단항 플러스·마이너스 연산자
단항 + 연산자는 피연산자의 값을 그대로 돌려줘요. 단항 - 연산자는 피연산자의 숫자 부정(negation)을 계산해요.
// 예제: snippets/shared/ArithmeticOperators.cs — UnaryPlusAndMinus
ulong 타입은 단항 - 연산자를 지원하지 않아요.
* 곱셈 연산자
곱셈 연산자 *는 피연산자들의 곱을 계산해요.
// 예제: snippets/shared/ArithmeticOperators.cs — Multiplication
단항 * 연산자는 포인터 역참조 연산자예요.
/ 나눗셈 연산자
나눗셈 연산자 /는 왼쪽 피연산자를 오른쪽 피연산자로 나눠요.
정수 나눗셈
정수 타입의 피연산자에서 / 연산자의 결과는 정수 타입이고, 두 피연산자의 몫을 0 방향으로 반올림한 값이에요.
// 예제: snippets/shared/ArithmeticOperators.cs — IntegerDivision
몫을 부동 소수점 숫자로 얻고 싶으면 float, double, decimal 타입을 쓰면 돼요.
// 예제: snippets/shared/ArithmeticOperators.cs — IntegerAsFloatingPointDivision
부동 소수점 나눗셈
float, double, decimal 타입에서 / 연산자는 두 피연산자의 몫을 돌려줘요.
// 예제: snippets/shared/ArithmeticOperators.cs — FloatingPointDivision
한쪽 피연산자가 decimal이면 다른 쪽은 float나 double이 될 수 없어요. float도 double도 decimal로의 암시적 변환이 없기 때문이죠. float나 double 피연산자를 decimal 타입으로 명시적으로 변환해야 해요. 숫자 타입 간 변환에 대한 자세한 내용은 기본 제공 숫자 변환을 참고하세요.
% 나머지 연산자
나머지 연산자 %는 왼쪽 피연산자를 오른쪽 피연산자로 나눈 뒤 남는 나머지를 계산해요.
정수 나머지
정수 타입의 피연산자에서 a % b의 결과는 $a - \frac{a}{b} \times b$로 계산한 값이에요. 0이 아닌 나머지의 부호는 왼쪽 피연산자의 부호와 같아요.
// 예제: snippets/shared/ArithmeticOperators.cs — IntegerRemainder
정수 나눗셈과 나머지 결과를 함께 구하려면 System.Math.DivRem 메서드를 쓰면 돼요.
부동 소수점 나머지
유한한 x, y에 대해 float와 double 피연산자의 x % y 결과는 다음 조건을 만족하는 값 z예요.
z의 부호(0이 아닐 때)는x의 부호와 같아요.z의 절댓값은 $|x| - n \times |y|$로 계산되는데, 여기서n은 $\frac{|x|}{|y|}$보다 작거나 같은 가장 큰 정수예요. 여기서 $|x|$와 $|y|$는 각각x와y의 절댓값을 나타내요.
[!NOTE] 이 나머지 계산 방식은 정수 피연산자에 쓰는 방식과 비슷하지만, IEEE 754 사양과는 달라요. IEEE 754 사양을 따르는 나머지 연산이 필요하면
System.Math.IEEERemainder메서드를 쓰세요.
유한하지 않은 피연산자에서 % 연산자가 어떻게 동작하는지는 C# 언어 사양의 나머지 연산자 절에서 확인할 수 있어요.
decimal 피연산자에서 나머지 연산자 %는 System.Decimal 타입의 나머지 연산자와 똑같이 동작해요.
다음 예제는 부동 소수점 피연산자에서 나머지 연산자가 어떻게 동작하는지 보여줘요.
// 예제: snippets/shared/ArithmeticOperators.cs — FloatingPointRemainder
+ 덧셈 연산자
덧셈 연산자 +는 피연산자들의 합을 계산해요.
// 예제: snippets/shared/ArithmeticOperators.cs — Addition
+ 연산자는 문자열 연결과 대리자(delegate) 결합에도 쓸 수 있어요. 자세한 내용은 + 및 += 연산자 문서를 참고하세요.
- 뺄셈 연산자
뺄셈 연산자 -는 왼쪽 피연산자에서 오른쪽 피연산자를 빼요.
// 예제: snippets/shared/ArithmeticOperators.cs — Subtraction
- 연산자로 대리자를 제거할 수도 있어요. 자세한 내용은 - 및 -= 연산자 문서를 참고하세요.
복합 할당
이항 연산자 op에 대해 다음 형태의 복합 할당 식은
x op= y
다음과 동일해요.
x = x op y
단, x는 딱 한 번만 평가된다는 점이 달라요.
다음 예제는 산술 연산자와 함께 복합 할당을 쓰는 방법을 보여줘요.
// 예제: snippets/shared/ArithmeticOperators.cs — CompoundAssignment
숫자 승격 때문에 op 연산의 결과가 x의 타입 T로 암시적으로 변환되지 않을 수 있어요. 그런 경우 op가 미리 정의된 연산자이고 연산 결과가 x의 타입 T로 명시적으로 변환 가능하다면, x op= y 형태의 복합 할당 식은 x = (T)(x op y)와 동일해요. 다만 x가 한 번만 평가된다는 점은 마찬가지예요. 다음 예제가 그 동작을 보여줘요.
// 예제: snippets/shared/ArithmeticOperators.cs — CompoundAssignmentWithCast
앞 예제에서 값 44는 값 300을 byte 타입으로 변환한 결과예요.
[!NOTE] checked 오버플로 검사 컨텍스트에서는 앞 예제가
System.OverflowException을 던져요. 자세한 내용은 정수 산술 오버플로 절을 참고하세요.
+=와 -= 연산자는 이벤트를 구독하고 구독 해지하는 데도 써요. 자세한 내용은 이벤트 구독·구독 해지 방법을 참고하세요.
연산자 우선순위와 결합성
다음 목록은 산술 연산자를 우선순위가 높은 그룹부터 낮은 순서로 나열한 거예요.
- (기본) 연산자: 후위 증가
x++, 후위 감소x-- - (단항) 연산자: 전위 증가
++x, 전위 감소--x, 단항+,- - (곱셈) 연산자:
*,/,% - (덧셈) 연산자: 이항
+,-
이항 산술 연산자는 왼쪽 결합이에요. 즉 컴파일러는 같은 우선순위 수준의 연산자를 왼쪽에서 오른쪽으로 평가해요.
우선순위와 결합성이 만든 평가 순서를 바꾸려면 괄호 ()를 쓰면 돼요.
// 예제: snippets/shared/ArithmeticOperators.cs — PrecedenceAndAssociativity
우선순위 수준 순서로 정렬한 C# 연산자 전체 목록은 C# 연산자 문서의 연산자 우선순위 절에서 확인할 수 있어요.
산술 오버플로와 0으로 나누기
산술 연산의 결과가 해당 숫자 타입이 표현할 수 있는 유한 값의 범위를 벗어나면, 산술 연산자의 동작은 피연산자의 타입에 따라 달라져요.
정수 산술 오버플로
정수를 0으로 나누면 항상 System.DivideByZeroException을 던져요.
정수 산술 오버플로가 발생하면 checked 또는 unchecked로 설정할 수 있는 오버플로 검사 컨텍스트가 결과 동작을 제어해요.
- checked 컨텍스트에서 상수 식에 오버플로가 발생하면 컴파일 타임 오류가 나요. 그 외에 런타임에 연산이 수행되면
System.OverflowException을 던져요. - unchecked 컨텍스트에서는 대상 타입에 맞지 않는 상위 비트를 버리고 결과를 잘라내요.
[!NOTE] 정수 나눗셈에는 unchecked 컨텍스트에서도
System.ArithmeticException을 던질 수 있는 특수한 경우가 있어요. 왼쪽 피연산자가 부호 있는 정수 타입의 최솟값(int.MinValue나long.MinValue)이고 오른쪽 피연산자가-1이면 결과를 대상 타입으로 표현할 수 없어요. 이 경우 .NET 런타임은System.ArithmeticException을 던져요.// 예제: snippets/shared/ArithmeticOperators.cs — IntegerDivisionOverflow
checked 및 unchecked 문과 함께 checked, unchecked 연산자로 식이 평가되는 오버플로 검사 컨텍스트를 제어할 수도 있어요.
// 예제: snippets/shared/ArithmeticOperators.cs — CheckedUnchecked
기본적으로 산술 연산은 unchecked 컨텍스트에서 수행돼요.
부동 소수점 산술 오버플로
float와 double 타입을 쓰는 산술 연산은 절대 예외를 던지지 않아요. 이 타입들의 산술 연산 결과는 무한대와 not-a-number를 나타내는 특수 값 중 하나일 수 있어요.
// 예제: snippets/shared/ArithmeticOperators.cs — FloatingPointOverflow
decimal 타입의 피연산자에서는 산술 오버플로가 항상 System.OverflowException을 던져요. 0으로 나누면 항상 System.DivideByZeroException을 던져요.
반올림 오류
실수의 부동 소수점 표현과 부동 소수점 산술의 일반적인 한계 때문에, 부동 소수점 타입을 쓰는 계산에서는 반올림 오류가 발생할 수 있어요. 식의 결과가 기대하는 수학적 결과와 다를 수 있죠. 다음 예제가 그런 경우를 몇 가지 보여줘요.
// 예제: snippets/shared/ArithmeticOperators.cs — RoundOffErrors
더 자세한 내용은 System.Double, System.Single, System.Decimal 참조 페이지의 설명을 참고하세요.
연산자 오버로드 가능성
사용자 정의 타입에 대해 단항(++, --, +, -) 및 이항(*, /, %, +, -) 산술 연산자를 오버로드할 수 있어요. 이항 연산자를 오버로드하면 대응하는 복합 할당 연산자도 암시적으로 오버로드돼요. C# 14부터 사용자 정의 타입이 복합 할당 연산자(op=)를 명시적으로 오버로드해 더 효율적인 구현을 제공할 수 있어요. 보통 타입이 이 연산자들을 오버로드하는 이유는 결과를 저장할 새 인스턴스를 할당하는 대신 값을 제자리에서 갱신할 수 있기 때문이에요. 타입이 명시적 오버로드를 제공하지 않으면 컴파일러가 암시적 오버로드를 생성해요.
사용자 정의 checked 연산자
산술 연산자를 오버로드할 때 checked 키워드로 그 연산자의 checked 버전을 정의할 수 있어요. 다음 예제가 그 방법을 보여줘요.
// 예제: snippets/shared/ArithmeticOperators.cs — CheckedOperator
checked 연산자를 정의할 때는 checked 한정자가 없는 대응 연산자도 함께 정의해야 해요. checked 컨텍스트는 checked 연산자를 호출하고, unchecked 컨텍스트는 checked 한정자가 없는 연산자를 호출해요.
연산자의 두 버전을 모두 정의할 때 동작이 달라지는 경우는 연산 결과가 결과 타입으로 표현하기에 너무 클 때뿐이에요.
- checked 연산자는
System.OverflowException을 던져요. checked한정자가 없는 연산자는 잘린 결과를 나타내는 인스턴스를 돌려줘요.
기본 제공 산술 연산자의 동작 차이에 대한 내용은 산술 오버플로와 0으로 나누기 절을 참고하세요.
checked 한정자는 다음 연산자들을 오버로드할 때만 쓸 수 있어요.
- 단항
++,--,-연산자 - 이항
*,/,+,-연산자 - 복합 할당
*=,/=,+=,-=연산자 (C# 14 이상) - 명시적 변환 연산자
[!NOTE]
checked한정자는 본문 안의 오버플로 검사 컨텍스트에는 영향을 주지 않아요. 기본 컨텍스트는 CheckForOverflowUnderflow 컴파일러 옵션의 값으로 결정돼요. 오버플로 검사 컨텍스트를 명시적으로 지정하려면checked및unchecked문을 쓰세요. 이 절 맨 앞의 예제처럼요.
C# 언어 사양
더 자세한 내용은 C# 언어 사양의 다음 절들을 참고하세요.
- 후위 증가·감소 연산자
- 전위 증가·감소 연산자
- 단항 플러스 연산자
- 단항 마이너스 연산자
- 곱셈 연산자
- 나눗셈 연산자
- 나머지 연산자
- 덧셈 연산자
- 뺄셈 연산자
- 복합 할당
- checked 및 unchecked 연산자
- 숫자 승격
- 사용자 정의 복합 할당