In this exercise, we extend the Fixed class from Exercise 00 to make it more functional. We add constructors for integer and floating-point inputs, and methods to convert the fixed-point value back to these types. Additionally, we overload the insertion (<<) operator to allow for easy printing of the fixed-point values.
- Create a new class called
Fixedthat represents a fixed-point number. - The class should have the following private members:
- An integer to store the fixed-point value.
- An integer to store the number of fractional bits.
- The class should have the following public members:
- A constructor that takes an integer and the number of fractional bits as arguments and initializes the fixed-point value.
- A constructor that takes a floating-point number and the number of fractional bits as arguments and initializes the fixed-point value.
- A method called
toIntthat converts the fixed-point value to an integer. - A method called
toFloatthat converts the fixed-point value to a floating-point number. - An overloaded insertion operator (
<<) that allows you to print the fixed-point value.
Fixed-point representation is a way to store fractional numbers by using an integer to represent the value, along with a predefined number of bits used for the fractional part. This method avoids floating-point arithmetic, which can be less efficient on some systems.
In the Fixed class, we use a fixed-point representation with _fractionalBits bits reserved for the fractional part. For example, if _fractionalBits is 8, it means the lower 8 bits of the integer will represent the fractional part, and the upper bits will represent the integer part.
float Fixed::toFloat(void) const {
return static_cast<float>(_fixedPointValue) / (1 << _fractionalBits);
}Explanation:
-
Fixed-point to float conversion: The
_fixedPointValueis an integer that represents the fixed-point number. To convert it to a floating-point number:- We need to shift the fixed-point value to the right by
_fractionalBitsto get the integer part. - The fractional part is obtained by dividing by
2^_fractionalBits(equivalent to1 << _fractionalBits).
- We need to shift the fixed-point value to the right by
-
Example:
- Let's assume
_fractionalBitsis 8. - If
_fixedPointValueis2560(in binary:00001010 00000000)- The upper 8 bits (
00001010) represent the integer part, which is10. - There are no bits set in the lower 8 bits, so the fractional part is
0.
- The upper 8 bits (
- The resulting floating-point value is
10.0.
- Let's assume
-
Step-by-Step:
- Convert
_fixedPointValueto float:static_cast<float>(_fixedPointValue). - Divide by
256(which is1 << 8):2560 / 256 = 10.0.
- Convert
int Fixed::toInt(void) const {
return _fixedPointValue >> _fractionalBits;
}Explanation:
-
Fixed-point to Integer Conversion: To get the integer part of the fixed-point number, we perform a right shift by
_fractionalBits. This operation discards the fractional part. -
Example:
- Using the same example where
_fixedPointValueis2560:- Shifting
2560(binary:00001010 00000000) right by 8 bits gives10(binary:00000000 00001010).
- Shifting
- The resulting integer value is
10.
- Using the same example where
-
Step-by-Step:
- Right shift
_fixedPointValueby_fractionalBits:2560 >> 8 = 10.
- Right shift
std::ostream& operator<<(std::ostream& os, const Fixed& fixed) {
os << fixed.toFloat();
return os;
}Explanation:
-
Output Stream Overloading: This function overloads the
<<operator forstd::ostreamto allow printing ofFixedobjects. -
Conversion to Float: When you use
std::cout << fixed, it calls thetoFloat()method to convert the fixed-point number to a floating-point number. -
Example:
- If
fixedrepresents the value10.0as discussed earlier:toFloat()returns10.0.- The
<<operator inserts10.0into the output stream.
- Step-by-Step:
- Call
fixed.toFloat(). - Insert the floating-point value into the stream
os. - Return the stream
osfor chaining operations.
- Call
int main() {
Fixed a(10.5f); // Initializes Fixed object with floating-point value
Fixed a(10.5f); // Initializes Fixed object with floating-point value 10.5
std::cout << "Fixed-point value as float: " << a << std::endl;
std::cout << "Fixed-point value as int: " << a.toInt() << std::endl;
return 0;
}Detailed Breakdown
-
Initialization:
- When
Fixed a(10.5f)is called, it uses theFixed(const float floating)constructor. - Inside this constructor:
Fixed::Fixed(const float floating) { std::cout << "Float constructor called" << std::endl; _fixedPointValue = roundf(floating * (1 << _fractionalBits)); }
- `floating` is `10.5`. - Assuming `_fractionalBits` is `8`, `1 << 8` is `256`. - The fixed-point representation is calculated as `roundf(10.5 * 256)`, which equals `2688`. - The `_fixedPointValue` is set to `2688`. - When
-
Printing the Fixed-point Value:
std::cout << "Fixed-point value as float: " << a << std::endl;- This calls the overloaded << operator:
std::ostream& operator<<(std::ostream& os, const Fixed& fixed) { os << fixed.toFloat(); return os; }
fixed.toFloat()converts the fixed-point value back to float:
float Fixed::toFloat(void) const { return static_cast<float>(_fixedPointValue) / (1 << _fractionalBits); }
- `_fixedPointValue` is `2688`. - Dividing by `256` gives `2688 / 256 = 10.5`. - The output is `10.5`. -
Printing the Fixed-point Value as Integer:
std::cout << "Fixed-point value as int: " << a.toInt() << std::endl;- This calls the
toIntmethod:
int Fixed::toInt(void) const {
return _fixedPointValue >> _fractionalBits;
}- Right-shifting
2688by8bits gives2688 >> 8 = 10. - The output is
10.