부동 소수점 숫자 형식
부동 소수점 숫자 형식 (C# 참조)
실수를 다룰 때 C#이 기본으로 제공하는 형식, 부동 소수점 숫자 형식에 대해 설명할게요. float, double, decimal 세 가지가 있고, 각각이 언제 어울리는지까지 함께 살펴볼게요.
출처: Microsoft Learn
본문
부동 소수점 숫자 형식은 실수를 나타내는 형식이에요. 이 형식들은 모두 값 형식(value types)이면서 단순 형식(simple types)이기도 해요. 그리고 리터럴을 사용해 초기화할 수 있죠. 모든 부동 소수점 숫자 형식은 산술 연산자, 비교 연산자, 같음 비교 연산자를 지원해요.
[!INCLUDEcsharp-version-note]
부동 소수점 형식의 특징
C#이 지원하는 미리 정의된 부동 소수점 형식은 다음과 같아요.
| C# 형식/키워드 | 대략적인 범위 | 정밀도 | 크기 | .NET 형식 |
|---|---|---|---|---|
float |
±1.5 x 10−45 ~ ±3.4 x 1038 | 약 6~9자리 | 4바이트 | xref:System.Single?displayProperty=nameWithType |
double |
±5.0 × 10−324 ~ ±1.7 × 10308 | 약 15~17자리 | 8바이트 | xref:System.Double?displayProperty=nameWithType |
decimal |
±1.0 x 10-28 ~ ±7.9228 x 1028 | 28~29자리 | 16바이트 | xref:System.Decimal?displayProperty=nameWithType |
위 표에서 가장 왼쪽 열의 C# 형식 키워드는 각각 대응하는 .NET 형식의 별칭(alias)이에요. 그래서 서로 바꿔 써도 되죠. 예를 들어 아래 두 선언은 같은 형식의 변수를 선언하는 거예요.
double a = 12.3;
System.Double b = 12.3;
각 부동 소수점 형식의 기본값은 0, 즉 0이에요. 그리고 각 형식에는 그 형식이 가질 수 있는 최솟값과 최댓값을 알려주는 MinValue, MaxValue 상수가 있어요. 여기에 더해 float와 double은 숫자가 아님(not-a-number)과 무한대(infinity)를 나타내는 상수도 제공해요. 예를 들어 double 형식에는 xref:System.Double.NaN?displayProperty=nameWithType, xref:System.Double.NegativeInfinity?displayProperty=nameWithType, xref:System.Double.PositiveInfinity?displayProperty=nameWithType 상수가 있어요.
decimal 형식은 필요한 정밀도가 소수점 오른쪽의 자릿수에 따라 결정되는 경우에 잘 맞아요. 이런 숫자는 금융 애플리케이션에서 흔히 쓰여요. 통화 금액(예: $1.00), 이자율(예: 2.625%) 같은 값들이 대표적이죠. 소수점 한 자리만 정확해도 되는 숫자조차 decimal이 더 정확하게 다뤄요. 예를 들어 0.1은 decimal 인스턴스로 정확히 표현되지만, 정확히 0.1을 나타내는 double이나 float 인스턴스는 존재하지 않아요. 이처럼 숫자 형식이 다르기 때문에, 소수 데이터에 double이나 float를 쓰면 산술 계산에서 예상치 못한 반올림 오류가 생길 수 있죠.
정확성을 보장하는 것보다 성능 최적화가 더 중요할 때는 decimal 대신 double을 써도 돼요. 다만 성능 차이는 극단적으로 계산량이 많은 애플리케이션에서나 체감할 수 있을 정도예요. decimal을 피할 또 다른 이유로는 저장 공간을 아끼고 싶을 때가 있어요. 예를 들어 ML.NET은 float을 쓰는데, 데이터셋이 아주 커지면 4바이트와 16바이트의 차이가 쌓여 크게 벌어지기 때문이에요. 자세한 내용은 xref:System.Decimal?displayProperty=nameWithType을 참고하세요.
표현식 안에서 정수(integral) 형식과 float, double 형식을 섞어 쓸 수 있어요. 이 경우 정수 형식은 어느 한 부동 소수점 형식으로 암시적으로 변환되고, 필요하면 float는 double로 암시적으로 변환돼요. 표현식의 계산 결과는 다음과 같이 정해져요.
- 표현식에
double형식이 있다면 그 표현식은double로 평가돼요. 다만 관계 비교나 같음 비교에서는bool로 평가되고요. - 표현식에
double형식이 없다면 그 표현식은float로 평가돼요. 다만 관계 비교나 같음 비교에서는bool로 평가되고요.
정수 형식과 decimal 형식도 한 표현식에서 섞어 쓸 수 있어요. 이 경우 정수 형식은 decimal로 암시적으로 변환되고, 표현식은 decimal로 평가되거나 관계 비교·같음 비교에서는 bool로 평가돼요.
반면 decimal 형식은 한 표현식 안에서 float나 double 형식과 섞을 수는 없어요. 산술·비교·같음 연산을 하고 싶다면 다음과 같이 피연산자를 decimal에서 변환하거나 decimal로 변환하는 명시적 변환을 꼭 해야 해요.
double a = 1.0;
decimal b = 2.1m;
Console.WriteLine(a + (double)b);
Console.WriteLine((decimal)a + b);
부동 소수점 값을 서식 지정할 때는 표준 숫자 서식 문자열이나 사용자 지정 숫자 서식 문자열을 사용할 수 있어요.
실수 리터럴 (Real literals)
실수 리터럴에 붙는 접미사(suffix)가 리터럴의 형식을 결정해요.
- 접미사가 없거나
d,D접미사가 붙은 리터럴은double이에요. f,F접미사가 붙은 리터럴은float예요.m,M접미사가 붙은 리터럴은decimal이에요.
각 형식의 예시를 보여드릴게요.
double d = 3D;
d = 4d;
d = 3.934_001;
float f = 3_000.5F;
f = 5.4f;
decimal myMoney = 3_000.5m;
myMoney = 400.75M;
위 예시는 _를 *자릿수 구분 기호(digit separator)*로 쓴 모습도 보여주고 있어요. 자릿수 구분 기호는 모든 종류의 숫자 리터럴에 사용할 수 있어요.
과학 표기법(scientific notation)도 쓸 수 있어요. 실수 리터럴에 지수부를 지정하는 방식이죠. 예시를 볼게요.
double d = 0.42e2;
Console.WriteLine(d); // output 42
float f = 134.45E-2f;
Console.WriteLine(f); // output: 1.3445
decimal m = 1.5E6m;
Console.WriteLine(m); // output: 1500000
형식 변환
부동 소수점 숫자 형식 사이에서 암시적 변환이 허용되는 건 딱 하나예요. 바로 float에서 double로의 변환이에요. 하지만 명시적 캐스트(cast)를 쓰면 어떤 부동 소수점 형식이든 다른 부동 소수점 형식으로 변환할 수 있어요. 자세한 내용은 기본 제공 숫자 변환을 참고하세요.
C# 언어 사양
더 자세한 내용은 C# 언어 사양의 다음 섹션을 참고하세요.