부동소수점 리터럴
부동소수점 리터럴 (Floating-point literals)
소스 파일에 값을 직접 적어서 컴파일 타임 상수를 만드는 부동소수점 리터럴을 알아볼게요. 3.14나 1e10처럼 소수점이 들어간 숫자 상수를 적는 규칙입니다.
출처: cppreference
본문
문법
digit-sequence decimal-exponent suffix (optional) (1)
digit-sequence . decimal-exponent (optional) suffix (optional) (2)
digit-sequence (optional) . digit-sequence decimal-exponent (optional) suffix (optional) (3)
0x | 0X hex-digit-sequence hex-exponent suffix (optional) (4) (since C++17)
0x | 0X hex-digit-sequence . hex-exponent suffix (optional) (5) (since C++17)
0x | 0X hex-digit-sequence (optional) . hex-digit-sequence hex-exponent suffix (optional) (6) (since C++17)
- (1) 소수점 없이 정수를 나타내는 digit-sequence. 이 경우 지수는 필수예요:
1e10,1e-5L - (2) 소수점을 가진 정수를 나타내는 digit-sequence. 이 경우 지수는 선택이에요:
1.,1.e-2 - (3) 분수를 나타내는 digit-sequence. 지수는 선택이에요:
3.14,.1f,0.1e-1L - (4) 기수 구분자(radix separator) 없이 정수를 나타내는 16진수 digit-sequence. 16진수 부동소수점 리터럴에서는 지수가 항상 필수예요:
0x1ffp10,0X0p-1 - (5) 기수 구분자를 가진 정수를 나타내는 16진수 digit-sequence. 16진수 부동소수점 리터럴에서는 지수가 항상 필수예요:
0x1.p0,0xf.p-1 - (6) 기수 구분자를 가진 분수를 나타내는 16진수 digit-sequence. 16진수 부동소수점 리터럴에서는 지수가 항상 필수예요:
0x0.123p-1,0xa.bp10l
decimal-exponent는 다음과 같은 형태를 가져요:
e | E exponent-sign (optional) digit-sequence
hex-exponent는 다음과 같은 형태를 가져요:
p | P exponent-sign (optional) digit-sequence (since C++17)
exponent-sign은 있으면 +나 - 중 하나예요.
suffix는 있으면 f, l, F, L, f16, f32, f64, f128, bf16, F16, F32, F64, F128, BF16(C++23부터) 중 하나이며, 부동소수점 리터럴의 타입을 결정해요:
- (접미사 없음) →
double fF→floatlL→long doublef16F16→std::float16_t,f32F32→std::float32_t,f64F64→std::float64_t,f128F128→std::float128_t,bf16BF16→std::bfloat16_t(C++23부터)
선택적인 작은따옴표(')를 숫자 사이에 구분자로 넣을 수 있으며, 리터럴의 값을 결정할 때는 무시돼요. (C++14부터)
설명
십진 과학 표기법이 사용돼요. 부동소수점 리터럴의 값은 significand(가수)에 10의 decimal-exponent승을 곱한 값이 됩니다. 예를 들어 123e4의 수학적 의미는 123×10⁴예요.
부동소수점 리터럴이 0x나 0X로 시작하면 16진수 부동소수점 리터럴이고, 그렇지 않으면 십진 부동소수점 리터럴이에요. (C++17부터) 16진수 부동소수점 리터럴에서는 significand가 16진수 유리수로 해석되고, 지수의 digit-sequence는 significand를 스케일링할 (십진) 정수 2의 거듭제곱으로 해석돼요. (C++17부터)
double d = 0x1.4p3; // hex fraction 1.4 (decimal 1.25) scaled by 2^3, that is 10.0
참고 사항
16진수 부동소수점 리터럴은 C++17 이전에는 C++의 일부가 아니었어요. 다만 C++11부터는 I/O 함수에서 파싱하고 출력할 수 있었죠. std::hexfloat를 켠 C++ I/O 스트림과 std::printf, std::scanf 등의 C I/O 스트림 모두 해당됩니다. 형식 설명은 std::strtof를 참고하세요.
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
| __cpp_hex_float | 201603L | (C++17) | 16진수 부동소수점 리터럴 |
예제
접미사가 타입을 어떻게 바꾸는지, 그리고 출력이 어떻게 달라지는지 확인해 볼게요.
#include <iomanip>
#include <iostream>
#include <limits>
#include <typeinfo>
#define OUT(x) '\n' << std::setw(16) << #x << x
int main()
{
std::cout
<< "Literal" "\t" "Printed value" << std::left
<< OUT( 58. ) // double
<< OUT( 4e2 ) // double
<< OUT( 123.456e-67 ) // double
<< OUT( 123.456e-67f ) // float, truncated to zero
<< OUT( .1E4f ) // float
<< OUT( 0x10.1p0 ) // double
<< OUT( 0x1p5 ) // double
<< OUT( 0x1e5 ) // integer literal, not floating-point
<< OUT( 3.14'15'92 ) // double, single quotes ignored (C++14)
<< OUT( 1.18e-4932l ) // long double
<< std::setprecision(39)
<< OUT( 3.4028234e38f ) // float
<< OUT( 3.4028234e38 ) // double
<< OUT( 3.4028234e38l ) // long double
<< '\n';
static_assert(3.4028234e38f == std::numeric_limits<float>::max());
static_assert(3.4028234e38f == // ends with 4
3.4028235e38f); // ends with 5
static_assert(3.4028234e38 != // ends with 4
3.4028235e38); // ends with 5
// Both floating-point constants below are 3.4028234e38
static_assert(3.4028234e38f != // a float (then promoted to double)
3.4028234e38); // a double
}
가능한 출력:
Literal Printed value
58. 58
4e2 400
123.456e-67 1.23456e-65
123.456e-67f 0
.1E4f 1000
0x10.1p0 16.0625
0x1p5 32
0x1e5 485
3.14'15'92 3.14159
1.18e-4932l 1.18e-4932
3.4028234e38f 340282346638528859811704183484516925440
3.4028234e38 340282339999999992395853996843190976512
3.4028234e38l 340282339999999999995912555211526242304
더 알아보기 (Learn more)
- 정수 리터럴 (integer literal) — 정수 상수를 적는 규칙
- 사용자 정의 리터럴 (user-defined literals) — 접미사를 직접 만드는 문법 (C++11부터)
- std::strtof — 부동소수점 문자열 파싱 형식