부동 상수
부동 상수 (Floating constant)
부동소수점 타입의 값을 표현식에 바로 쓸 수 있게 해 주는 게 부동 상수예요.
출처: cppreference
본문
문법 (Syntax)
부동 상수는 비-lvalue 표현식으로, 다음과 같은 형태를 가져요.
significand exponent(선택) suffix(선택)
여기서 significand(가수부)의 형태는 다음과 같아요.
whole-number(선택) .(선택) fraction(선택)
exponent(지수부)의 형태는 다음과 같아요.
| e|E exponent-sign(선택) digit-sequence | (1) |
|---|---|
| p|P exponent-sign(선택) digit-sequence | (2) (C99부터) |
- 십진 부동소수점 상수의 지수 문법이에요.
- 십육진 부동소수점 상수의 지수 문법이에요.
(C23부터) 숫자 사이 구분자로 작은따옴표(')를 넣을 수 있는데, 컴파일할 때는 무시돼요.
설명 (Explanation)
(C99부터) 가수부가 0x 또는 0X로 시작하면 그 부동 상수는 십육진 부동소수점 상수예요. 그렇지 않으면 십진 부동소수점 상수예요.
십육진 부동소수점 상수에서는 가수부가 십육진 유리수로 해석되고, 지수의 숫자 나열은 가수부를 스케일할 2의 정수 거듭제곱으로 해석돼요.
double d = 0x1.2p3; // hex fraction 1.2 (decimal 1.125) scaled by 2^3, that is 9.0
십진 부동소수점 상수에서는 가수부가 십진 유리수로 해석되고, 지수의 숫자 나열은 가수부를 스케일할 10의 정수 거듭제곱으로 해석돼요.
double d = 1.2e3; // decimal fraction 1.2 scaled by 10^3, that is 1200.0
접미사 (Suffixes)
접미사가 없는 부동 상수는 타입 double이에요. 접미사가 f 또는 F면 float, l 또는 L이면 long double 타입이 돼요.
(C23부터) 구현이 매크로 __STDC_IEC_60559_BFP__를 미리 정의하면 다음 접미사와 해당 부동 상수가 추가로 지원돼요.
- 접미사
df또는DF면 타입이_Decimal32예요. - 접미사
dd또는DD면 타입이_Decimal64예요. - 접미사
dl또는DL이면 타입이_Decimal128이에요.
십진 부동소수점 타입용 접미사는 십육진 부동 상수에서는 허용되지 않아요.
선택적 부분 (Optional parts)
지수가 있고 소수부를 쓰지 않는다면, 소수점 기호를 생략할 수 있어요.
double x = 1e0; // floating-point 1.0 (period not used)
십진 부동 상수에서는 지수부가 선택이에요. 지수를 생략하면 소수점은 필수가 되고, 가수부의 전체 숫자(whole-number) 부분이나 소수(fraction) 부분 중 하나는 반드시 있어야 해요.
double x = 1.; // floating-point 1.0 (fractional part optional)
double y = .1; // floating-point 0.1 (whole-number part optional)
(C99부터) 십육진 부동 상수에서는 지수가 선택이 아니에요. f 접미사가 십육진 숫자로 오인되는 모호함을 피하려는 거예요.
표현 가능한 값 (Representable values)
부동 상수를 평가한 결과는 구현이 정한 방식으로 고르는 값이에요. 가장 가까운 표현 가능한 값이거나, 그 바로 옆의 크거나 작은 값이에요(즉, 번역 중 기본 반올림 방향은 구현 정의).
같은 소스 형태의 모든 부동 상수는 같은 내부 형식과 값으로 변환돼요. 1.23과 1.230처럼 소스 형태가 다른 부동 상수는 같은 내부 형식과 값으로 변환될 필요는 없어요.
(C99부터) 부동소수점 상수는 FLT_EVAL_METHOD가 지시하면 자신의 타입이 나타내는 것보다 더 넓은 범위와 정밀도로 변환될 수 있어요. 예를 들어 상수 0.1f는 어떤 표현식에서 0.1L처럼 작동할 수 있어요.
(C99부터) FLT_RADIX가 2라면 십육진 부동 상수를 평가한 결과는 그 부동 상수가 나타내는 정확한 값을 목표 타입으로 올바르게 반올림한 값이에요.
(C23부터) 같은 수치 값 x를 갖지만 *양자 지수(quantum exponent)*가 다른 십진 부동소수점 타입의 상수들, 예를 들어 1230.dd, 1230.0dd, 1.23e3dd는 구별되는 내부 표현을 가져요.
십진 부동소수점 타입의 부동 상수의 양자 지수 q는, 가능할 때 가수부의 마지막 자리 자리에 10^q가 1을 나타내도록 정해져요. 위에서 정한 양자 지수 q와 계수 c = x·10^-q를 상수 타입에 정확히 표현할 수 없으면, 타입 한도 내에서 필요에 따라 q를 늘리고 c를 그에 맞춰 줄여요. 필요한 반올림이 생길 수 있고, 그 결과가 0이나 무한대가 될 수도 있어요. q가 최대값에 도달한 뒤에도 (반올림된) c가 여전히 허용 범위를 벗어나면, 결과 부동 상수는 양의 무한대 값을 가져요.
참고 (Notes)
부동 상수를 내부 표현으로 변환할 때는 기본 반올림 방향과 정밀도가 적용돼요. 그리고 #pragma STDC FENV_ACCESS가 적용 중이어도 부동소수점 예외는 발생하지 않아요(실행 시간에 문자열을 변환할 때는 strtod를 쓸 수 있어요). 이 점이 부동 타입의 산술 상수 표현식과는 다르다는 걸 기억해 두면 좋아요.
부동 상수의 글자는 대소문자를 구분하지 않아요(C23부터는 십진 부동소수점 타입용 접미사에 대문자와 소문자를 함께 쓸 수 없다는 점만 예외). 0x1.ep+3과 0X1.EP+3은 같은 부동소수점 값 15.0을 나타내요.
setlocale이 지정하는 소수점은 부동 상수의 문법에 아무 영향이 없어요. 소수점 문자는 항상 마침표(period)예요.
정수와 달리 모든 부동 값이 십진(C99부터는 십육진) 상수 문법으로 직접 표현될 수 있는 건 아니에요. C99부터는 매크로 NAN과 INFINITY, 그리고 nan 같은 함수가 그런 특수 값을 만드는 방법을 제공해요. IEEE float NaN처럼 보이는 0x1.FFFFFEp128f는 사실 그 형식에서 무한대로 오버플로된다는 점도 참고하세요.
음의 부동 상수는 없어요. -1.2 같은 표현은 산술 연산자 단항 마이너스를 부동 상수 1.2에 적용한 것이에요. 특수 값 음의 영(negative zero)은 -0.0으로 만들 수 있어요.
예시
#include <stdio.h>
int main(void)
{
printf("15.0 = %a\n", 15.0);
printf("0x1.ep+3 = %f\n", 0x1.ep+3);
// Constants outside the range of type double.
printf("+2.0e+308 --> %g\n", 2.0e+308);
printf("+1.0e-324 --> %g\n", 1.0e-324);
printf("-1.0e-324 --> %g\n", -1.0e-324);
printf("-2.0e+308 --> %g\n", -2.0e+308);
}
출력:
15.0 = 0x1.ep+3
0x1.ep+3 = 15.000000
+2.0e+308 --> inf
+1.0e-324 --> 0
-1.0e-324 --> -0
-2.0e+308 --> -inf
더 알아보기
- 정수 상수와 문자 상수 문서를 함께 보면 리터럴 전반이 정리돼요.
- 반올림 방향과 평가 정밀도 문서에서 부동 상수가 내부 표현으로 변환될 때의 동작을 볼 수 있어요.
- cppreference의 부동 상수 원문에서 C++의 부동 리터럴과 비교해 볼 수 있어요.