부동 소수점 숫자 (floating point numbers)
정수(integer)도 매우 좋지만 때때로 매우 큰 숫자나 소수점이 있는 숫자를 저장해야 하는 경우도 있다. 부동 소수점(floating point) 자료형 변수는 4320.0, -3.33, 0.01226과 같은 실수를 저장하는 변수다. 부동 소수점은 소수점이 "부동(float)" 할 수 있다는 것을 의미한다. 즉, 소수점 앞과 뒤에 있는 자릿수를 지원한다.
C++에는 float
, double
및 long double
과 같은 부동 소수점 자료형이 있다. 정수와 마찬가지로 C++은 이러한 자료형의 크기를 지정하지 정의하지 않았다. 현대 아키텍처에서 부동 소수점 표현은 거의 항상 IEEE 754 바이너리 형식을 따른다. 이 형식에서 float은 4 byte이고 double은 8이며 long double은 8, 12, 16 byte 중 하나다.
float 자료형은 항상 부호가 있다. (signed)
Category | Type | Minimum Size | Typical Size |
---|---|---|---|
floating point | float | 4 bytes | 4 bytes |
double | 8 bytes | 8 bytes | |
long double | 8 bytes | 8, 12, or 16 bytes |
다음은 부동 소수점 숫자에 대한 몇가지 정의다.
float fValue;
double dValue;
long double dValue2;
부동 소수점 숫자 리터럴을 사용할 때는 정수값과 구분할 수 있도록 소수점 이하 자리를 포함하는 것이 일반적이다.
int x(5); // 5 means integer
double y(5.0); // 5.0 is a floating point literal (no suffix means double type by default)
float z(5.0f); // 5.0 is a floating point literal, f suffix means float type
f접미사는 부동 소수점 숫자의 리터럴을 나타내는 데 사용한다.
정밀도와 범위 (Precision and range)
분수 1/3을 생각해보자. 이 숫자의 10진수 표현은 0.3333333333...으로 3이 무한대로 나온다. 무한 길이의 숫자를 저장하려면 무한한 메모리가 필요하지만 부동 소수점 자료형은 일반적으로 4 or 8 byte다. 결국, 부동 소수점 숫자는 특정 유효 자릿수까지만 저장하고 나머지는 손실된다. 부동 소수점의 정밀도(precision)는 정보 손실 없이 나타낼 수 있는 유의한 자릿수를 정의한다.
부동 소수점 숫자를 출력할 때 std::cout의 기본 정밀도는 6이다. 즉, 모든 부동 소수점 숫자는 6자리까지만 유의하다고 가정하여 이후는 잘라낸다.
#include <iostream>
int main()
{
float f;
f = 9.87654321f; // f suffix means this number should be treated as a float
std::cout << f << std::endl;
f = 987.654321f;
std::cout << f << std::endl;
f = 987654.321f;
std::cout << f << std::endl;
f = 9876543.21f;
std::cout << f << std::endl;
f = 0.0000987654321f;
std::cout << f << std::endl;
return 0;
}
output:
9.87654
987.654
987654
9.87654e+006
9.87654e-005
각각 6자리 숫자다.
그러나 <iomanip>
헤더 파일에 정의된 std:setprecision()
함수를 사용해서 cout에서 출력되는 기본 정밀도를 재정의(override)할 수 있다.
#include <iostream>
#include <iomanip> // for std::setprecision()
int main()
{
std::cout << std::setprecision(16); // show 16 digits
float f = 3.33333333333333333333333333333333333333f;
std::cout << f << std::endl;
double d = 3.3333333333333333333333333333333333333;
std::cout << d << std::endl;
return 0;
}
output:
3.333333253860474
3.333333333333334
정밀도를 16자리로 설정했으므로 위의 각 숫자는 16자리로 출력된다. 하지만 그 숫자들이 16자리까지 모두 정확하지는 않다.
부동 소수점 자료형의 변수가 가지는 정밀도는 자릿수 크기('float
은 double
보다 작다.)와 저장되는 특정 값에 따라 달라진다. float
값의 정밀도는 6 ~ 9 자리다. double
값의 정밀도는 15 ~ 18 자리로 16. long double
은 얼마나 많은 바이트를 차지하느냐에 따라 최소 15, 18 또는 33의 자리 정밀도를 가진다.
정밀도 문제는 단지 소수점 이하의 숫자에만 영향을 미치는 것이 아니라, 너무 많은 유효 숫자에도 영향을 미친다. 큰 숫자를 고려해 보자.
#include <iostream>
#include <iomanip> // for std::setprecision()
int main()
{
float f(123456789.0f); // f has 10 significant digits
std::cout << std::setprecision(9); // to show 9 digits in f
std::cout << f << std::endl;
return 0;
}
Output:
123456792
123456792는 123456789보다 크다. 값 123456789.0은 10자리 숫자이지만 float
은 일반적으로 7자리 정밀도를 가지기 때문에 정밀도를 잃고, 예상하지 못한 결과가 출력한다.
따라서 변수가 보유 할 수가 있는 것보다 더 정밀도가 필요한 부동 소수점 숫자를 사용할 때는 주의해야 한다.
Size | Range | Precision |
---|---|---|
4 bytes | ±1.18 x 10-38 to ±3.4 x 1038 | 6-9 significant digits, typically 7 |
8 bytes | ±2.23 x 10-308 to ±1.80 x 10308 | 15-18 significant digits, typically 16 |
80-bits (12 bytes) | ±3.36 x 10-4932 to ±1.18 x 104932 | 18-21 significant digits |
16 bytes | ±3.36 x 10-4932 to ±1.18 x 104932 | 33-36 significant digits |
반올림 오류 (Rounding errors)
부동 소수점 숫자가 까다로운 이유 중 하나는 binary(2진수: 데이터 저장 방법)와 10진법(사람이 생각하는 방식) 간의 차이가 크기 때문이다. 1/10을 생각해보자. 소수로, 이것은 0.1이며 0.1은 쉽게 표현할 수 있는 숫자로 생각한다. 그러나 binaray(2진수)에서 0.1은 0.00011001100110011…와 같은 무한 시퀀스로 표현된다. 이 때문에 부동 소수점 숫자에 0.1을 지정하면 정밀도 문제가 발생한다.
#include <iostream>
#include <iomanip> // for std::setprecision()
int main()
{
double d(0.1);
std::cout << d << std::endl; // use default cout precision of 6
std::cout << std::setprecision(17);
std::cout << d << std::endl;
return 0;
}
This outputs:
0.1
0.10000000000000001 (: rounding error)
정밀도를 17로 설정했다. 메모리 제한으로 인해 근사치를 잘라내야 했기 때문에 그 결과 수치는 정확하게 0.1이 아니다. 이를 반올림 오류(rounding error)라고 한다.
반올림 오류(rounding error)는 예상치 못한 결과를 초래하기도 한다.
#include <iostream>
#include <iomanip> // for std::setprecision()
int main()
{
std::cout << std::setprecision(17);
double d1(1.0);
std::cout << d1 << std::endl;
double d2(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1); // should equal 1.0
std::cout << d2 << std::endl;
}
This outputs:
1
0.99999999999999989 (: rounding error)
NaN and Inf
부동 소수점 숫자에는 특별한 두 가지 숫자가 있다. 첫 번째는 무한대를 나타내는 Inf
다. 두 번째는 숫자가 아님을 나타내는 NaN
이다.
#include <iostream>
int main()
{
double zero = 0.0;
double posinf = 5.0 / zero; // positive infinity
std::cout << posinf << std::endl;
double neginf = -5.0 / zero; // negative infinity
std::cout << neginf << std::endl;
double nan = zero / zero; // not a number (mathematically invalid)
std::cout << nan << std::endl;
return 0;
}
This outputs:
1.#INF
-1.#INF
1.#IND
INF은 무한대를 의미하고 IND는 중간을 의미한다. Inf 및 NaN 출력 결과는 플랫폼마다 다르므로 결과가 다를 수 있다.
포스트의 원문은 http://www.learncpp.com/cpp-tutorial/25-floating-point-numbers/ 입니다.
출처: https://boycoding.tistory.com/152?category=1007180 [소년코딩]
'프로그래밍 언어 > [ C++ ]' 카테고리의 다른 글
[ C++ ] 05. std::vector (0) | 2021.02.08 |
---|---|
[ C++ ] 04. std::array (0) | 2021.02.08 |
[ C++ ] 03. auto (0) | 2021.02.05 |
[ C++ ] 02. 난수 생성 (0) | 2021.02.05 |
[ C++ ] 00. cout, cin, endl (0) | 2021.02.04 |