Mentions légales du service

Skip to content
Snippets Groups Projects
Commit 6b9038e8 authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Update solution to exercise 30.

parent 3ab7df15
No related tags found
No related merge requests found
#include <iostream> #include <iostream>
#include <sstream> // for std::ostringstream #include <cassert>
#include <string>
#include <cmath> // for std::round #include <cmath> // for std::round
#include <sstream>
class PowerOfTwoApprox
{
public:
//! Compute the best possible approximation of `value` with `Nbits`
PowerOfTwoApprox(int Nbits, double value);
//! \return The approximation as a floating point.
operator double() const;
//! Accessor to numerator.
int GetNumerator() const;
//! Accessor to exponent.
int GetExponent() const;
private:
int numerator_ {};
int exponent_ {};
};
std::ostream& operator<<(std::ostream& out, const PowerOfTwoApprox& approximation)
{
out << approximation.GetNumerator() << " / 2^" << approximation.GetExponent();
return out;
}
//! Returns `number` * (2 ^ `exponent`) //! Returns `number` * (2 ^ `exponent`)
...@@ -21,71 +54,52 @@ int TimesPowerOf2(int number, int exponent) ...@@ -21,71 +54,52 @@ int TimesPowerOf2(int number, int exponent)
return number; return number;
} }
/*!
* \brief Multiply the approximate representation by an integer.
*
* \param[in] coefficient Integer coefficient by which the object is multiplied.
*
* \return An approximate integer result of the multiplication.
*/
int operator*(int coefficient, const PowerOfTwoApprox& approx)
{
return TimesPowerOf2(approx.GetNumerator() * coefficient, -approx.GetExponent());
}
//! Round to `x` the nearest integer.
int RoundAsInt(double x) int operator*(const PowerOfTwoApprox& approx, int coefficient)
{ {
return static_cast<int>(std::round(x)); return coefficient * approx;
} }
// Maximum integer that might be represented with `Nbits` bits.
int MaxInt(int Nbits) int PowerOfTwoApprox::GetNumerator() const
{ {
return (TimesPowerOf2(1, Nbits) - 1); return numerator_;
} }
//! Class to group the two integers used in the approximation we define. int PowerOfTwoApprox::GetExponent() const
class PowerOfTwoApprox
{ {
public: return exponent_;
}
//! Compute the best possible approximation of `value` with `Nbits`
PowerOfTwoApprox(int Nbits, double value);
//! \return The approximation as a floating point.
explicit operator double() const;
//! Accessor to numerator.
int GetNumerator() const;
//! Accessor to exponent.
int GetExponent() const;
private:
int numerator_ {};
int exponent_ {};
};
std::ostream& operator<<(std::ostream& out, const PowerOfTwoApprox& approximation) //! Round to `x` the nearest integer.
int RoundAsInt(double x)
{ {
out << approximation.GetNumerator() << "/2^" << approximation.GetExponent(); return static_cast<int>(std::round(x));
return out;
} }
/*! // Maximum integer that might be represented with `Nbits` bits.
* \brief Multiply the approximate representation by an integer. int MaxInt(int Nbits)
* {
* \param[in] coefficient Integer coefficient by which the object is multiplied. return (TimesPowerOf2(1, Nbits) - 1);
* }
* \return An approximate integer result of the multiplication.
*/
int operator*(int coefficient, const PowerOfTwoApprox& approx)
{
return TimesPowerOf2(approx.GetNumerator() * coefficient, -approx.GetExponent());
}
int operator*(const PowerOfTwoApprox& approx, int coefficient)
{
return coefficient * approx;
}
//! Helper function that computes numerator and denominator. //! Helper function that computes numerator and denominator.
...@@ -99,13 +113,15 @@ void HelperComputePowerOf2Approx(double value, int exponent, int& numerator, int ...@@ -99,13 +113,15 @@ void HelperComputePowerOf2Approx(double value, int exponent, int& numerator, int
} }
//! Compute the best possible approximation of `value` with `Nbits`
PowerOfTwoApprox::PowerOfTwoApprox(int Nbits, double value) PowerOfTwoApprox::PowerOfTwoApprox(int Nbits, double value)
{ {
int max_numerator = MaxInt(Nbits); int max_numerator = MaxInt(Nbits);
int& numerator = numerator_; int& numerator = numerator_; // alias!
int& exponent = exponent_; int& exponent = exponent_;
numerator = 0; numerator = 0;
exponent = 0; exponent = 0;
int denominator {}; int denominator {};
...@@ -124,25 +140,13 @@ PowerOfTwoApprox::PowerOfTwoApprox(int Nbits, double value) ...@@ -124,25 +140,13 @@ PowerOfTwoApprox::PowerOfTwoApprox(int Nbits, double value)
PowerOfTwoApprox::operator double() const PowerOfTwoApprox::operator double() const
{ {
int denominator = TimesPowerOf2(1, exponent_); // As we're here in a method definition the data attributes could have been kept!
return static_cast<double>(numerator_) / denominator; // But using accessor gives more safety: it clearly states the values are only read as accessors are const.
int denominator = TimesPowerOf2(1, GetExponent());
return static_cast<double>(GetNumerator()) / denominator;
} }
int PowerOfTwoApprox::GetNumerator() const
{
return numerator_;
}
int PowerOfTwoApprox::GetExponent() const
{
return exponent_;
}
enum class RoundToInteger { no, yes };
class TestDisplay class TestDisplay
{ {
...@@ -152,13 +156,20 @@ public: ...@@ -152,13 +156,20 @@ public:
TestDisplay(int resolution); TestDisplay(int resolution);
//! To make TestDisplay an abstract class. //! To make TestDisplay an abstract class.
virtual ~TestDisplay(); virtual ~TestDisplay() = 0;
//! Pure virtual method Do(). //! Get the resolution.
int GetResolution() const;
//! Functor.
virtual void operator()(int Nbits) const = 0; virtual void operator()(int Nbits) const = 0;
protected: protected:
//! Convenient enum used in \a PrintLine().
enum class RoundToInteger { no, yes };
/*! /*!
* \brief Print a line with information about error. * \brief Print a line with information about error.
...@@ -174,11 +185,25 @@ protected: ...@@ -174,11 +185,25 @@ protected:
private: private:
//! Resolution. //! Resolution.
const int resolution_; const int resolution_; // `const` ensures here that it is defined in the constructor!
}; };
void TestDisplay::PrintLine(int Nbits, double exact, double approx,
std::string optional_string1, std::string optional_string2,
RoundToInteger do_round_to_integer) const
{
int error = RoundAsInt(GetResolution() * std::fabs(exact - approx) / exact);
std::cout << "[With " << Nbits << " bits]: " << optional_string1
<< (do_round_to_integer == RoundToInteger::yes ? RoundAsInt(exact) : exact) << " ~ " << approx
<< optional_string2
<< " [error = " << error << "/" << GetResolution() << "]"
<< std::endl;
}
TestDisplay::TestDisplay(int resolution) TestDisplay::TestDisplay(int resolution)
: resolution_(resolution) : resolution_(resolution)
{ } { }
...@@ -187,39 +212,26 @@ TestDisplay::TestDisplay(int resolution) ...@@ -187,39 +212,26 @@ TestDisplay::TestDisplay(int resolution)
TestDisplay::~TestDisplay() = default; TestDisplay::~TestDisplay() = default;
void TestDisplay::operator()(int Nbits) const int TestDisplay::GetResolution() const
{ {
static_cast<void>(Nbits); // neutralize warning about unused argument at no runtime cost. return resolution_;
} }
void TestDisplay::PrintLine(int Nbits, double exact, double approx,
std::string optional_string1, std::string optional_string2,
RoundToInteger do_round_to_integer) const
{
int error = RoundAsInt(resolution_ * std::fabs(exact - approx) / exact);
std::cout << "[With " << Nbits << " bits]: " << optional_string1
<< (do_round_to_integer == RoundToInteger::yes ? RoundAsInt(exact) : exact) << " ~ " << approx
<< optional_string2
<< " [error = " << error << "/" << resolution_ << "]"
<< std::endl;
}
class TestDisplayPowerOfTwoApprox : public TestDisplay class TestDisplayPowerOfTwoApprox : public TestDisplay
{ {
public: public:
//! Constructor.
TestDisplayPowerOfTwoApprox(int resolution); TestDisplayPowerOfTwoApprox(int resolution);
virtual ~TestDisplayPowerOfTwoApprox() override = default;
//! Destructor //! Display the output for the chosen `Nbits`.
virtual ~TestDisplayPowerOfTwoApprox() override; virtual void operator()(int Nbits) const override = 0;
//! Pure virtual method Do().
virtual void operator()(int Nbits)const override = 0;
protected: protected:
//! Method in charge of the actual display. //! Method in charge of the actual display.
...@@ -230,9 +242,8 @@ protected: ...@@ -230,9 +242,8 @@ protected:
TestDisplayPowerOfTwoApprox::TestDisplayPowerOfTwoApprox(int resolution) TestDisplayPowerOfTwoApprox::TestDisplayPowerOfTwoApprox(int resolution)
: TestDisplay(resolution) : TestDisplay(resolution)
{ } { }
TestDisplayPowerOfTwoApprox::~TestDisplayPowerOfTwoApprox() = default;
void TestDisplayPowerOfTwoApprox::Display(int Nbits, double value) const void TestDisplayPowerOfTwoApprox::Display(int Nbits, double value) const
...@@ -240,86 +251,67 @@ void TestDisplayPowerOfTwoApprox::Display(int Nbits, double value) const ...@@ -240,86 +251,67 @@ void TestDisplayPowerOfTwoApprox::Display(int Nbits, double value) const
PowerOfTwoApprox approximation(Nbits, value); PowerOfTwoApprox approximation(Nbits, value);
const double approx = static_cast<double>(approximation); const double approx = static_cast<double>(approximation);
std::ostringstream oconv; std::ostringstream oconv;
oconv << " (" << approximation << ")"; oconv << " (" << approximation << ")";
PrintLine(Nbits, value, approx, "", oconv.str());
PrintLine(Nbits, value, approx, "", oconv.str(), RoundToInteger::no);
} }
class TestDisplayPowerOfTwoApprox065 : public TestDisplayPowerOfTwoApprox class TestDisplayPowerOfTwoApprox065 : public TestDisplayPowerOfTwoApprox
{ {
public: public:
//! Constructor.
TestDisplayPowerOfTwoApprox065(int resolution);
//! Destructor,
~TestDisplayPowerOfTwoApprox065() override;
//! Display the output for the chosen `Nbits`.
void operator()(int Nbits)const override;
};
TestDisplayPowerOfTwoApprox065::TestDisplayPowerOfTwoApprox065(int resolution)
: TestDisplayPowerOfTwoApprox(resolution)
{ }
TestDisplayPowerOfTwoApprox065(int resolution);
TestDisplayPowerOfTwoApprox065::~TestDisplayPowerOfTwoApprox065() = default; virtual ~TestDisplayPowerOfTwoApprox065() override = default;
void operator()(int Nbits) const override;
void TestDisplayPowerOfTwoApprox065::operator()(int Nbits) const };
{
Display(Nbits, 0.65);
}
class TestDisplayPowerOfTwoApprox035 : public TestDisplayPowerOfTwoApprox class TestDisplayPowerOfTwoApprox035 : public TestDisplayPowerOfTwoApprox
{ {
public: public:
//! Constructor.
TestDisplayPowerOfTwoApprox035(int resolution); TestDisplayPowerOfTwoApprox035(int resolution);
//! Destructor, virtual ~TestDisplayPowerOfTwoApprox035() override = default;
~TestDisplayPowerOfTwoApprox035() override;
void operator()(int Nbits) const override;
//! Display the output for the chosen `Nbits`.
void operator()(int Nbits)const override;
}; };
TestDisplayPowerOfTwoApprox065::TestDisplayPowerOfTwoApprox065(int resolution)
: TestDisplayPowerOfTwoApprox(resolution)
{ }
TestDisplayPowerOfTwoApprox035::TestDisplayPowerOfTwoApprox035(int resolution) TestDisplayPowerOfTwoApprox035::TestDisplayPowerOfTwoApprox035(int resolution)
: TestDisplayPowerOfTwoApprox(resolution) : TestDisplayPowerOfTwoApprox(resolution)
{ } { }
TestDisplayPowerOfTwoApprox035::~TestDisplayPowerOfTwoApprox035() = default;
void TestDisplayPowerOfTwoApprox065::operator()(int Nbits) const
{
Display(Nbits, 0.65);
}
void TestDisplayPowerOfTwoApprox035::operator()(int Nbits) const void TestDisplayPowerOfTwoApprox035::operator()(int Nbits) const
{ {
Display(Nbits, 0.35); Display(Nbits, 0.35);
} }
class TestDisplaySumOfMultiply : public TestDisplay class TestDisplaySumOfMultiply : public TestDisplay
{ {
public: public:
//! Constructor.
TestDisplaySumOfMultiply(int resolution); TestDisplaySumOfMultiply(int resolution);
//! To make the class a concrete one.
virtual ~TestDisplaySumOfMultiply() override;
//! Display the output for the chosen `Nbits`. //! Display the output for the chosen `Nbits`.
void operator()(int Nbits)const override; void operator()(int Nbits) const override;
private: private:
...@@ -333,8 +325,6 @@ TestDisplaySumOfMultiply::TestDisplaySumOfMultiply(int resolution) ...@@ -333,8 +325,6 @@ TestDisplaySumOfMultiply::TestDisplaySumOfMultiply(int resolution)
: TestDisplay(resolution) : TestDisplay(resolution)
{ } { }
TestDisplaySumOfMultiply::~TestDisplaySumOfMultiply() = default;
void TestDisplaySumOfMultiply::operator()(int Nbits) const void TestDisplaySumOfMultiply::operator()(int Nbits) const
{ {
...@@ -349,129 +339,124 @@ void TestDisplaySumOfMultiply::Display(int Nbits, double value1, int coefficient ...@@ -349,129 +339,124 @@ void TestDisplaySumOfMultiply::Display(int Nbits, double value1, int coefficient
PowerOfTwoApprox approximation1(Nbits, value1); PowerOfTwoApprox approximation1(Nbits, value1);
PowerOfTwoApprox approximation2(Nbits, value2); PowerOfTwoApprox approximation2(Nbits, value2);
// int approx = coefficient1 * approximation1 + coefficient2 * approximation2; int approx = approximation1 * coefficient1 + coefficient2 * approximation2;
int approx = approximation1 * coefficient1 + coefficient2 * approximation2;
std::ostringstream oconv;
oconv << value1 << " * " << coefficient1 << " + " << value2 << " * " << coefficient2 << " = ";
PrintLine(Nbits, exact, static_cast<double>(approx), oconv.str(), "", RoundToInteger::yes);
}
std::ostringstream oconv;
oconv << value1 << " * " << coefficient1
<< " + " << value2 << " * " << coefficient2 << " = ";
//! Function for error handling. We will see later how to fulfill the same functionality more properly. PrintLine(Nbits, exact, approx, oconv.str(), "", RoundToInteger::yes);
[[noreturn]] void Error(std::string explanation)
{
std::cout << "ERROR: " << explanation << std::endl;
exit(EXIT_FAILURE);
} }
class TestDisplayContainer class TestDisplayContainer
{ {
public: public:
//! Constructor.
TestDisplayContainer(std::size_t capacity); TestDisplayContainer(std::size_t capacity);
//! Destructor.
~TestDisplayContainer(); ~TestDisplayContainer();
//! Add a new test_display_register. //! Add a new test_display_register.
//! At each call, the item to be registered is put at the first available position and internal current_position_ //! At each call, the item to be registered is put at the first available position and internal current_position_
//! is incremented. If the end-user attempts to register more than three items, the error() function is called. //! is incremented. If the end-user attempts to register more than three items, the Error() function is called.
void Register(TestDisplay* test_display); void Register(TestDisplay* test_display);
//! Accessor to the i-th object in the container. //! Returns the i-th element. If element is invalid, call `Error()`.
const TestDisplay& operator[](std::size_t i) const; const TestDisplay& operator[](std::size_t i) const;
//! Get the number of elements actually stored in the class (nullptr don't count). //! Return the actual number of elements in the container.
std::size_t GetSize() const; std::size_t GetSize() const;
private: private:
//! Get the maximal number of elements that might be stored in the container. //! Returns the maximum number of elements the container may carry.
std::size_t GetCapacity() const; std::size_t GetCapacity() const;
private: private:
//! Maximum number of items that might be registered in the container.
const std::size_t capacity_;
//! List of all known `TestDisplay` objects. //! List of all known `TestDisplay` objects.
TestDisplay** list_; TestDisplay** list_;
//! Index to place the next register object. If '3', no more object may be registered. //! Index to place the next register object. If '3', no more object may be registered.
//! std::size_t is used as it is the convention within the STL for array indexes.
std::size_t current_position_ {}; std::size_t current_position_ {};
//! Capacity.
const std::size_t capacity_;
}; };
TestDisplayContainer::TestDisplayContainer(std::size_t capacity)
: capacity_(capacity) //! Function for error handling. We will see later how to fulfill the same functionality more properly.
[[noreturn]] void Error(std::string explanation)
{ {
std::cout << "ERROR: " << explanation << std::endl;
exit(EXIT_FAILURE);
}
TestDisplayContainer::TestDisplayContainer(std::size_t capacity)
: capacity_ { capacity }
{
list_ = new TestDisplay*[capacity]; list_ = new TestDisplay*[capacity];
for (auto i = 0ul; i < capacity; ++i) for (auto index = 0ul; index < capacity; ++index)
list_[i] = nullptr; list_[index] = nullptr;
} }
TestDisplayContainer::~TestDisplayContainer() TestDisplayContainer::~TestDisplayContainer()
{ {
for (auto i = 0ul; i < capacity_; ++i) for (auto index = 0ul; index < capacity_; ++index)
delete list_[i]; delete list_[index];
delete[] list_; // don't forget the [] to delete an array! delete[] list_; // don't forget the [] to delete an array!
} }
void TestDisplayContainer::Register(TestDisplay* test_display) void TestDisplayContainer::Register(TestDisplay* test_display)
{ {
if (current_position_ >= capacity_) assert(current_position_ <= GetCapacity());
Error("TestDisplayContainer is already full; impossible to register a new element!");
if (current_position_ == GetCapacity())
list_[current_position_] = test_display; Error("There are already three elements stored in the contained; can't take more!");
++current_position_;
list_[current_position_++] = test_display;
} }
const TestDisplay& TestDisplayContainer::operator[](std::size_t i) const void Loop(int initial_Nbit, int final_Nbit, int increment_Nbit, const TestDisplayContainer& container)
{ {
if (i >= GetCapacity()) const auto size = container.GetSize();
Error("You try to access an element out of bounds!"); for (auto i = 0ul; i < size; ++i)
{
if (list_[i] == nullptr) // equivalent to i >= GetSize() for (int nbits = initial_Nbit; nbits <= final_Nbit; nbits += increment_Nbit)
Error("You try to access an element that was never initialized!"); container[i](nbits);
return *list_[i];
}
std::cout << std::endl;
}
}
std::size_t TestDisplayContainer::GetCapacity() const std::size_t TestDisplayContainer::GetCapacity() const
{ {
return capacity_; return capacity_;
} }
std::size_t TestDisplayContainer::GetSize() const std::size_t TestDisplayContainer::GetSize() const
{ {
return current_position_; return current_position_;
} }
//! For each container stored, loop oover all those bits and print the result on screen. const TestDisplay& TestDisplayContainer::operator[](std::size_t i) const
void Loop(int initial_Nbit, int final_Nbit, int increment_Nbit, const TestDisplayContainer& container)
{ {
for (auto i = 0ul; i < container.GetSize(); ++i) if (i >= GetSize())
{ Error("You requested an element out of bounds!");
decltype(auto) current_test_display = container[i];
assert(list_[i] != nullptr); // should be true if first condition is right!
for (int nbits = initial_Nbit; nbits <= final_Nbit; nbits += increment_Nbit)
current_test_display(nbits); return *(list_[i]);
std::cout << std::endl;
}
} }
...@@ -484,7 +469,7 @@ int main(int argc, char** argv) ...@@ -484,7 +469,7 @@ int main(int argc, char** argv)
static_cast<void>(argc); // to silence warning about unused argc - don't bother static_cast<void>(argc); // to silence warning about unused argc - don't bother
static_cast<void>(argv); // to silence warning about unused argv - don't bother static_cast<void>(argv); // to silence warning about unused argv - don't bother
TestDisplayContainer container(3); TestDisplayContainer container(3ul);
container.Register(new TestDisplayPowerOfTwoApprox065(1000000)); container.Register(new TestDisplayPowerOfTwoApprox065(1000000));
container.Register(new TestDisplayPowerOfTwoApprox035(1000000)); container.Register(new TestDisplayPowerOfTwoApprox035(1000000));
...@@ -494,4 +479,3 @@ int main(int argc, char** argv) ...@@ -494,4 +479,3 @@ int main(int argc, char** argv)
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment