CPP Module 02 - ex01
ex01
다음과 같이 동작하는 프로그램을 작성하라.
- 기존 ex00
에서 사용한 코드를 재사용한다.
- constructor
: 인자를 받아 이를 고정 소수점 값으로 변환해주는 생성자를 추가하라. 이 때 사용하는 fractional bits
의 값은 ex00
와 동일하다.
- float toFloat(void) const
: 고정 소수점 값을 부동 소수점 값으로 변환한다.
- int toInt(void) const
: 고정 소수점 값을 정수 값으로 변환한다.
- operator<<
: 고정 소수점 값의 부동 소수점 표현을 출력 스트림에 입력하는 <<
연산자에 대한 오버로드이다.
main.cpp
와 그 실행결과는 다음과 같다.
부동 소수점과 고정 소수점
부동 소수점
부동 소수점은 흔히 사용하는 float
, double
에서 사용하는 방식이다.
부동 소수점은 위 그림과 같이 세 가지 파트로 구분된다.
- Sign - 부호 비트
- Exponent - 지수부
- mantissa - 가수부(과제에서는
fraction
으로 표현)
이에 대해 살펴보기 전 정규화(Normalization)
을 알아야 한다.
정규화(Normalization)
정규화
는 프로그래밍에서 여러 의미를 갖지만, 여기에서는 2진수의 양식을
1.10101010.... * 2^n
과 같이 정수부에 1만 남기고 나머지를 실수부에 넣는 방식이다.
부동 소수점은 이렇게 정규화된 값을 지수부와 가수부에 나누어 저장한다.
부호 비트 - 1비트
부호 비트는 0이면 양수, 1이면 음수를 의미한다.
지수부 - 8비트
지수의 경우 2^n
과 같거나 가장 가까운 숫자로 결정된다.
2진수로 계산하기 때문에 지수부에는 n
을 저장하면 되는데, 여기서 지수 그 자체를 저장하는 것이 아닌 bias
와 더한 값을 저장한다.
bias
의 경우 (2^n-1) - 1
로 구할 수 있으며, 32비트를 기준으로는 (2^8-1) - 1 = 127
이 된다.
가수부 - 나머지
정규화를 진행한 부분 중 실수부만을 저장한다.
그 이유는 2진수를 정규화하면 정수부는 무조건 1이 되기 때문이며, 보통 이를 생략하기 때문이다.
그래서 만약 -17
이라는 값을 32비트 부동 소수점으로 표현한다고 하면, 그 과정은
- 정규화를 진행한다.
17
을 2진수로 변환하면10001
이며, 이를 정규화 하면1.0001 * 2^4
가 된다. - 부호 비트를 결정한다.
-17
이기 때문에, 부호 비트는 1이 된다. - 지수부에 저장할
bias
를 계산한다.
17
을 2진수로 변환한 뒤 이를 정규화한 값은1.0001 * 2^4
이므로, 지수는 4이며bias
는 127이므로127 + 4 = 131
을 지수부에 저장하게 되며, 이를 2진수로 변환하면10000011
이다. - 가수부에 저장할 값을 계산한다.
정규화한 값중 허수부를 넣으면 되기 때문에, 남은 자리인 23칸에0001
을 적절하게 저장한다. 즉00010000000000000000000
이 될 것이다.
이러한 과정을 거치게 된다.
그러므로 -17
을 부동 소수점으로 표현하면 11000001100010000000000000000000
가 된다.
고정 소수점
고정 소수점은 소수 부분을 표현할 비트의 수를 미리 정해둔 뒤에 10진수를 2진수로 변환한 값을 그대로 비트에 넣은 것이다.
만약 16비트를 사용하고, 7.625
라는 실수를 고정 소수점(소수를 표현할 비트는 8)을 통해 저장하고자 한다면, 7.625
를 2진수로 변환하면 111.101
이 되기 때문에
위 사진과 동일하게 저장된다.
고정 소수점으로 변환
과제에서 요구하는 Fractional Bits
는 8이므로, 이를 토대로 변환하면 된다.
정수를 고정 소수점으로 변환
기존의 값을 고정 소수점으로 변환하고자 하면, Fractional Bits
가 존재할 수 있도록 8 비트를 고정으로 가지고 있어야 하기 때문에 <<
연산을 통해 빈 비트 공간을 만들면 된다.
즉 변환하고자 하는 값을 n
이라고 한다면
n << 8
과 같이 변환하면 된다.
실수를 고정 소수점으로 변환
이 경우는 정수와 동일하게 <<
연산을 통해 Fractional Bits
의 공간을 마련해주면 된다.
다만 여기서 주의할 점으로, float
나 double
과 같은 부동 소수점의 경우 <<
과 같은 쉬프트 연산 자체가 불가능하기 때문에, 이를 우회하여 표현해야 한다.
또한 현재 고정 소수점으로 저장할 클래스의 변수의 타입이 int
형 이므로 일부 데이터가 누락될 수 있다.
그러므로 비교적 정확한 변환을 위해 허용 함수인 roundf
를 통해 반올림을 진행할 필요가 있다.
즉 변환하고자 하는 값을 n
이라고 한다면
roundf(n * (1 << 8))
과 같이 변환하면 된다.
고정 소수점을 정수로 변환
이 경우는 특정 값을 고정 소수점으로 변환한 것을 다시 원위치 시키면 된다.
즉 변환하고자 하는 값을 n
이라고 한다면
n >> 8
과 같이 변환하면 된다.
고정 소수점을 실수로 변환
과제에서 이 경우는 부동 소수점 -> 고정 소수점 -> 부동 소수점
의 변환 방식을 의미한다.
그러므로 기존 Fixed
클래스에 저장하고 있는 고정 소수점을 float
로 강제 형변환하여 부동 소수점으로 변환한 뒤, 고정 소수점의 Fractional Bits
를 표현하기 위해 <<
연산한 값을 다시 되돌리면 된다.
즉 변환하고자 하는 값을 n
이라고 한다면
float(n) / (1 << 8)
과 같이 변환하면 된다.
<< 연산자 오버로딩
<<
연산자가 사용된 코드와 그 실행 결과를 확인하면 알 수 있다.
Fixed const b( 10 );
Fixed const c( 42.42f );
std::cout << "b is " << b << std::endl;
std::cout << "c is " << c << std::endl;
해당 코드의 동작 결과는
b is 10
c is 42.4219
이므로 Fixed
클래스에 대한 <<
연산은 Fixed
클래스에 저장된 고정 소수점 값을 toFloat
함수를 통해 부동 소수점으로 출력하는 것을 확인할 수 있다.
코드
Fixed.hpp
#ifndef FIXED_HPP
# define FIXED_HPP
# include <iostream>
# include <cmath>
class Fixed {
public:
Fixed(void);
Fixed(const int fixed);
Fixed(const float fixed);
Fixed(const Fixed &src);
~Fixed(void);
Fixed & operator=(Fixed const &src);
int getRawBits(void) const;
void setRawBits(int const raw);
int toInt(void) const;
float toFloat(void) const;
private:
int _value;
const static int _bits;
};
std::ostream & operator<<(std::ostream & s, const Fixed & fixed);
#endif
Fixed.cpp
#include "Fixed.hpp"
const int Fixed::_bits = 8;
Fixed::Fixed(void) {
_value = 0;
std::cout << "Default constructor called" << std::endl;
}
Fixed::Fixed(const int value)
{
_value = value << _bits;
std::cout << "Int constructor called" << std::endl;
}
Fixed::Fixed(const float value)
{
_value = roundf(value * (1 << _bits));
std::cout << "Float constructor called" << std::endl;
}
Fixed::Fixed(const Fixed &src)
{
std::cout << "Copy constructor called" << std::endl;
*this = src;
}
Fixed::~Fixed(void)
{
std::cout << "Destrutor called" << std::endl;
}
Fixed& Fixed::operator=(const Fixed &src)
{
std::cout << "Assignation operation called" << std::endl;
_value = src._value;
return (*this);
}
int Fixed::getRawBits(void) const
{
return (_value);
}
void Fixed::setRawBits(int const raw)
{
_value = raw;
}
int Fixed::toInt(void) const
{
return (_value >> _bits);
}
float Fixed::toFloat(void) const
{
return (float(_value) / (1 << _bits));
}
std::ostream& operator<<(std::ostream &out, const Fixed &fixed)
{
out << fixed.toFloat();
return out;
}
실행 결과는 다음과 같다.
참조
Introduction of Floating Point Representation
Fixed Point vs. Floating Point Illustration
컴퓨터에서의 실수 표현: 고정소수점 vs 부동소수점
Introduction of Floating Point Representation
Author And Source
이 문제에 관하여(CPP Module 02 - ex01), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@appti/CPP-Module-02-ex01저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)