일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- #define
- 프로그래밍
- Machine Learning
- 형변환
- classification problem
- 이코테
- coursera
- 본즈앤올
- decimal
- #endif
- const
- 학습 알고리즘
- C++
- 기계학습
- standford University
- 코딩테스트
- 기계학습 기초
- 나동빈님
- algorithm
- 코드블럭 오류
- Runtime constants
- Andrew Ng
- 연산자
- compile time constants
- 단항연산자
- sizeof()
- Greedy
- 홍정모님
- regression problem
- CLion
- Today
- Total
wellcome_공부일기
C++ | 02.05. 부동소수점 수(Floating Point Numbers) 본문
<목차>
1. 부동소수점 수(Floating Point Numbers)란?
2. 부동소수점 수 출력하기(Printing floating point numbers)
3. 부동소수점의 정밀도(Floating point precision)
4. float와 double & long double의 최대 최소 크기
5. 부동 소수점(Floating point data types) 사용 시 주의 사항
부동소수점 수(Floating Point Numbers)란?
- 정수는 전체 숫자를 세는 데는 좋지만, 때로 우리는 매우 큰 숫자, 즉 분수 성분(fractional component)을 가진 숫자를 저장이 필요할 때가 있습니다.
- 부동 소수점 유형 변수는 4320.0, -3.33 또는 0.01226과 같은 실제 숫자를 보유할 수 있는 변수입니다.
- 부동 소수점은 소수점이 "부동"할 수 있다는 사실 즉 소수점 전후의 변수 자릿수를 지원할 수 있다는 사실을 말합니다.
부동 소수점 데이터 유형에는 < float, double, long double > 세 가지가 있습니다.
정수와 마찬가지로 C++는 이러한 유형의 실제 크기를 정의하지 않습니다(그러나 최소 크기를 보장합니다). 현대 아키텍처에서 부동 소수점 표현은 거의 항상 IEEE 754 이진 형식을 따릅니다. 이 형식에서 float는 4바이트, double은 8이며, long double은 double(8바이트), 80비트(12바이트로 패딩하는 경우가 많다) 또는 16바이트와 같을 수 있습니다.
- 부동소수점 자료형(Floating point data types)
부동소수점 자료형은 항상 signed형을 가집니다.(양수와 음수 값을 취할 수 있습니다.)
부동소수점을 아래와 같이 선언할 수 있습니다.
float fValue;
double dValue;
long double ldValue;
부동 소수점 리터럴(Literal)을 사용할 때는 항상 최소 하나의 소수점 자리(소수가 0인 경우에도)를 포함해야 합니다.
컴파일러에게 숫자 정수가 아닌 부동 소수점의 숫자라는 것을 알려주어야 하기 때문입니다.
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
- 기본적으로, 부동소수 리터럴은 double 타입을 기본으로 합니다
- f 접미사는 문자 그대로의 float 타입의 literal을 나타내기 위해 사용된다. .
- 항상 리터럴 유형이 할당되거나 초기화에 사용되는 변수의 유형과 일치하는지 확인해야 합니다.
- 그렇지 않으면, 불필요한 변환이 발생하여 정밀도가 상실될 수 있습니다.
- 부동 소수점 리터럴을 사용해야 하는 곳에 정수 리터럴을 사용하면 안됩니다.
- 여기에는 부동 소수점 객체에 값을 초기화하거나 할당할 때, 부동 소수점 산술, 부동 소수점 값을 기대하는 호출 함수가 포함됩니다.
부동소수점 수 출력하기(Printing floating point numbers)
#include <iostream>
int main()
{
std::cout << 5.0 << '\n'; //output: 5
std::cout << 6.7f << '\n'; //output: 6.7
std::cout << 9876543.21 << '\n'; //output: 9.87654e+06
}
- 첫번째 사례에서, std::cout이 우리가 5.0이라고 타이핑을 했음에도, 5를 출력하였습니다.
- 기본적으로 std::cout은 숫자의 분수(소수점) 부분이 0이면 이를 출력하지 않기 때문입니다.
- 두번째 사례에서, 우리가 의도한 바와 같이 잘 출력된 것을 확인할 수 있습니다.
- 세번째 사례에서, 과학적 표기법(scientific notation)로 숫자가 출력되었는데, 이는 물리와 수학 계산 시 많이 사용하는 표기법입니다. (후에, scientific notation을 포스팅할 예정!)
부동소수점의 정밀도(Floating point precision)
- 분수 1/3을 생각해보면, 이 숫자의 소수점은 0.333333333333333333333333333333333)이고 3은 무한대로 나갑니다.
- 컴퓨터에서, 무한 길이의 숫자를 저장하기 위해 무한대의 메모리가 필요로 할 것입니다.
- 하지만, 일반적으로 float와 double, long double은 4 또는 8바이트밖에 가지고 있지 않습니다.
- 제한된 메모리때문에 부동 소수점 자리가 특정 수의 유의한 숫자만 저장할 수 있고, 추가 유의한 숫자가 손실된다는 것을 의미합니다.
- 실제로 저장되는 숫자는 원하는 숫자에 가까울 것 같지만 정확할 수 없다는 것을 의미합니다
부동 소수점 숫자의 정밀도(Floating point precision)는 정보 손실 없이 얼마나 많은 유의한 자릿수를 나타낼 수 있는지를 말합니다.
부동 소수점 번호를 출력할 때 std::cout은 기본 정밀도가 6으로 즉, 모든 부동 소수점 변수가 6자리(부동수의 최소 정밀도)까지만 유의하다고 가정하고, 따라서 그 후에는 무엇이든지 잘리게 됩니다.
#include <iostream>
int main()
{
std::cout << 9.87654321f << '\n'; //output: 9.87654
std::cout << 987.654321f << '\n'; //output: 987.654
std::cout << 987654.321f << '\n'; //output: 987654
std::cout << 9876543.21f << '\n'; //output: 9.87654e+006
std::cout << 0.0000987654321f << '\n'; //output: 9.87654e-005
return 0;
}
- 각 숫자는 6개의 유의한 숫자(significant digit)만 가지고 있습니다.
- 일부 경우에는 std::cout이 과학적 표기법(scientific notation)으로 출력 숫자로 전환됩니다.
- 부동 소수점 변수(floating point variable)가 갖는 정밀도의 자릿수는 크기와 저장되는 특정 값(일부 값은 다른 값보다 정밀도가 더 높음)에 따라 달라집니다.
- float가 double보다 2배 낮은 정밀도를 가집니다.
- float 값은 6~9자리의 정밀도를 가지며, 대부분의 플로트 값은 최소 7자리의 유의한 숫자를 가집니다.
- double 값은 15~18자리의 정밀도를 가지며, 대부분의 이중 값은 최소 16자리의 유의한 자릿수를 가집니다.
- long double은 얼마나 많은 바이트를 점유하느냐에 따라 최소 정밀도가 15, 18 또는 33의 유의 자릿수를 가집니다.
- 아래의 코드 예에서 확인해봅시다!
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
float f(123456789.0f);
cout << std ::setprecision(16) << endl;
cout << 1.0 /3.0 << endl; //output: 0.3333333333333333
cout << f << endl; //output: 123456792
double d(0.1);
cout << d << endl; //output: 0.1
cout << std ::setprecision(17);
cout << d << endl; //output: 0.10000000000000001
double d1(0.1);
double d2(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1);
cout << std ::setprecision(17);
cout << d1 << endl; //output: 0.10000000000000001
cout << d2 << endl; //output: 0.99999999999999989
return 0;
}
- <iomanip>은 입출력을 숫자를 조절하기 위해 포함되었습니다. ex. std::setprecision(#)
- 첫번째 초기화된 float f를 보면 10개의 유의미한 숫자(significant digit)를 가지지만, 출력을 해보면 7개 자리의 숫자가 유의미하게 출력됩니다.
- 이진수를 이용해서 컴퓨터가 저장하기 때문에 우리가 생각한 것과 다른 결과가 나옵니다.
- std::setprecision(#)은 #자리까지의 소수점 정밀도를 가진다고 설정하는 것으로, 1.0/3.0이 0.3333333333333333으로 출력됩니다.
- 두번째로 초기화된 double d(0.1)은 정밀도를 17자리 숫자까지 설정하고 출력하면, 0.10000000000000001이 나옵니다.
- 이는 0.1에 가장 가까운 숫자가 0.10000000000000001라는 것을 알 수 있습니다.
-세번째로 초기화된 double d2(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1)은 우리가 의도한 1.0이 아닌 0.99999999999999989이 출력되는데, 이는 0.1이 한번씩 더해질 때마다 오차가 누적된 결과입니다.
- 최근에 나오는 언어들은 double을 사용하지만, 아직까지는 사이즈가 2배가 되는데 cpu가속도 차이, 연산속도나 메모리면에서 부담이 될 수 있습니다.
- 그래서 파이썬으로 딥러닝을 할 때, 숫자를 많이 다루므로 float를 사용하는게 더 편합니다.
- 위에서 언급한바와 같이 내부적으로 부동소수점 수가 저장되는 방식에는 한계가 있기 때문에 float를 많이 사용합니다.
float와 double & long double의 최대 최소 크기
#include <iostream>
#include <limits>
using namespace std;
int main()
{
float f;
double d;
long double ld;
cout << sizeof(float) << endl; //output: 4
cout << sizeof(d) << endl; //output: 8
cout << sizeof(ld) << endl; //output: 16
cout << numeric_limits<float>:: max() << endl; //output: 3.40282e+38
cout << numeric_limits<double >:: max() << endl; //output: 1.79769e+308
cout << numeric_limits<long double>:: max() << endl; //output: 1.18973e+4932
cout << numeric_limits<float>:: min() << endl; //output: 1.17549e-38
cout << numeric_limits<double >:: min() << endl; //output: 2.22507e-308
cout << numeric_limits<long double>:: min() << endl; //output: 3.3621e-4932
cout << numeric_limits<float>:: lowest() << endl; //output: -3.40282e+38
cout << numeric_limits<double >:: lowest() << endl; //output: -1.79769e+308
cout << numeric_limits<long double>:: lowest() << endl; //output: -1.18973e+4932
return 0;
}
- <limits>를 포함하여 최대 최소 크기를 알아낼 수 있습니다.
- min은 표현할 수 있는 가장 작은 숫자를 절대값으로 알려줍니다.
- 절대값 대신 가장 작은 음수를 알고 싶을 때 lowest를 이용합니다.
부동 소수점(Floating point data types) 사용 시 주의 사항
#include <iostream>
using namespace std;
int main()
{
double zero = 0.0;
double posinf = 5.0 / zero;
double neginf = -5.0 / zero;
double nan = zero / zero;
cout << posinf << endl; //output: inf
cout << neginf << endl; //output: -inf
cout << nan << endl; //output: nan
return 0;
}
- 숫자가 아닌 숫자들끼리의 실수는 나누면 안됩니다.
- posinf는 positive infinite를, neginf는 negative infinite를, nan은 not a number를 의미합니다.
- inf는 infinite로 무한대를 의미합니다.
- 위 문제는 std :: isnan사용해서 해결할 수 있습니다!
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
double zero = 0.0;
double posinf = 5.0 / zero;
double neginf = -5.0 / zero;
double nan = zero / zero;
cout << posinf << " " << std::isnan(posinf) << endl; //output: inf 0
cout << neginf << " " << std::isnan(neginf) << endl; //output: -inf 0
cout << posinf << " " << std::isinf(posinf) << endl; //output: inf 1
cout << neginf << " " << std::isinf(neginf) << endl; //output: -inf 1
cout << nan << " " << std::isnan(nan) << endl; //output: nan 1
cout << 1.0 << " " << std::isnan(1.0) << endl; //output: 1 0
return 0;
}
- std :: isnan사용을 위해 <cmath>를 포함시켜줍니다.
- std :: isnan은 "숫자가 아니다."라는 것을 의미하고, 1이 나온다면, 정말 숫자가 아니고, 0이 나온다면 숫자임을 알려줍니다.
* 해당 글은 홍정모님의 따라 배우는 C++과 learncpp 사이트를 공부하여 작성한 포스팅입니다.
'프로그래밍 > C++' 카테고리의 다른 글
C++ | 02.07. 문자 자료형(Char Data Type) (0) | 2020.05.08 |
---|---|
C++ | 02.06 Boolean 자료형과 조건문 if (0) | 2020.05.07 |
02.04. C++ | 무치형(Void Data Types) = No Type! (0) | 2020.05.05 |
02.03. C++ | 고정너비 정수(Fixed-width integers)란? (0) | 2020.05.04 |
02.02. C++ | 정수형(Integer Data Types) (0) | 2020.05.03 |