Commit 37e9dac6 authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Modify the object programming TP. The numer has seen reduced and many have been reworded.

parent 49c11388
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Object programming](/notebooks/2-ObjectProgramming/0-main.ipynb)
%% Cell type:markdown id: tags:
* [Introduction to the concept of object](/notebooks/2-ObjectProgramming/1-Introduction.ipynb)
* [TP 3](/notebooks/2-ObjectProgramming/1b-TP.ipynb)
* [TP 4](/notebooks/2-ObjectProgramming/1b-TP.ipynb)
* [Member functions](/notebooks/2-ObjectProgramming/2-Member-functions.ipynb)
* [TP 4](/notebooks/2-ObjectProgramming/2b-TP.ipynb)
* [TP 5](/notebooks/2-ObjectProgramming/2b-TP.ipynb)
* [Base constructors and destructor](/notebooks/2-ObjectProgramming/3-constructors-destructor.ipynb)
* [TP 5](/notebooks/2-ObjectProgramming/3b-TP.ipynb)
* [TP 6](/notebooks/2-ObjectProgramming/3b-TP.ipynb)
* [Encapsulation](/notebooks/2-ObjectProgramming/4-encapsulation.ipynb)
* [TP 6](/notebooks/2-ObjectProgramming/4b-TP.ipynb)
* [TP 7](/notebooks/2-ObjectProgramming/4b-TP.ipynb)
* [Static attributes](/notebooks/2-ObjectProgramming/5-static.ipynb)
* [Inheritance and polymorphism](/notebooks/2-ObjectProgramming/6-inheritance.ipynb)
* [TP 7](/notebooks/2-ObjectProgramming/6b-TP.ipynb)
* [TP 8](/notebooks/2-ObjectProgramming/6b-TP.ipynb)
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Object programming](/notebooks/2-ObjectProgramming/0-main.ipynb) - [TP 1](/notebooks/2-ObjectProgramming/1b-TP.ipynb)
# [Getting started in C++](/) - [Object programming](/notebooks/2-ObjectProgramming/0-main.ipynb) - [TP 4](/notebooks/2-ObjectProgramming/1b-TP.ipynb)
%% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#Introduction" data-toc-modified-id="Introduction-1">Introduction</a></span></li><li><span><a href="#EXERCICE-13:-introduce-PowerOfTwoApprox-structure" data-toc-modified-id="EXERCICE-13:-introduce-PowerOfTwoApprox-structure-2">EXERCICE 13: introduce <code>PowerOfTwoApprox</code> structure</a></span></li></ul></div>
%% Cell type:markdown id: tags:
### Introduction
[This notebook](/notebooks/TP/HowTo.ipynb) explains very briefly your options to run the TP.
`initial_file.cpp` is the solution to the exercice 10 (we drop the writing into an external file and the function pointers).
### EXERCICE 13: introduce `PowerOfTwoApprox` structure
We will tackle the same problem with a dedicated `struct` called `PowerOfTwoApprox` which will be used to represent a real by two integers: the numerator and the exponent to the power of 2 used in the denominator.
Create this structure and use it throughout the program (`compute_power_of_2_approx()` signature should for instance be modified accordingly).
The expected output is unchanged from the initial file:
````
[With 2 bits]: 0.65 ~ 3 / 2^2 (0.75) [error = 15/100]
[With 4 bits]: 0.65 ~ 10 / 2^4 (0.625) [error = 4/100]
[With 6 bits]: 0.65 ~ 42 / 2^6 (0.65625) [error = 1/100]
[With 8 bits]: 0.65 ~ 166 / 2^8 (0.648438) [error = 0/100]
[With 2 bits]: 0.35 ~ 3 / 2^3 (0.375) [error = 7/100]
[With 4 bits]: 0.35 ~ 11 / 2^5 (0.34375) [error = 2/100]
[With 6 bits]: 0.35 ~ 45 / 2^7 (0.351562) [error = 0/100]
[With 8 bits]: 0.35 ~ 179 / 2^9 (0.349609) [error = 0/100]
[With 1 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 2965 [error = 254/1000]
[With 2 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4448 [error = 119/1000]
[With 3 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4008 [error = 8/1000]
[With 4 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3857 [error = 30/1000]
[With 5 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3967 [error = 2/1000]
[With 6 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4004 [error = 7/1000]
[With 7 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3977 [error = 0/1000]
[With 8 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3968 [error = 2/1000]
````
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Object programming](/notebooks/2-ObjectProgramming/0-main.ipynb) - [TP 4](/notebooks/2-ObjectProgramming/2b-TP.ipynb)
# [Getting started in C++](/) - [Object programming](/notebooks/2-ObjectProgramming/0-main.ipynb) - [TP 5](/notebooks/2-ObjectProgramming/2b-TP.ipynb)
%% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#EXERCICE-14:-transform-compute_power_of_2_approx()-into-a-PowerOfTwoApprox-method" data-toc-modified-id="EXERCICE-14:-transform-compute_power_of_2_approx()-into-a-PowerOfTwoApprox-method-1">EXERCICE 14: transform <code>compute_power_of_2_approx()</code> into a <code>PowerOfTwoApprox</code> method</a></span></li><li><span><a href="#EXERCICE-15:-Add-a-method-to-give-away-the-double-value" data-toc-modified-id="EXERCICE-15:-Add-a-method-to-give-away-the-double-value-2">EXERCICE 15: Add a method to give away the double value</a></span></li></ul></div>
<div class="toc"><ul class="toc-item"><li><span><a href="#EXERCICE-14:-transform-compute_power_of_2_approx()-into-a-PowerOfTwoApprox-method" data-toc-modified-id="EXERCICE-14:-transform-compute_power_of_2_approx()-into-a-PowerOfTwoApprox-method-1">EXERCICE 14: transform <code>compute_power_of_2_approx()</code> into a <code>PowerOfTwoApprox</code> method</a></span></li></ul></div>
%% Cell type:markdown id: tags:
### EXERCICE 14: transform `compute_power_of_2_approx()` into a `PowerOfTwoApprox` method
What we did in exercice 1 is a bit clunky: the object is introduced but we keep spending time fetching the data attribute to act upon them (even initialization to 0 is done manually several times).
We'll streamline this a bit by making some of the functions methods of the `PowerOfTwoApprox` struct; starting with `compute_power_of_2_approx()` that will become a method named `Compute()`. This method will change the internal data attributes rather than changing an output argument.
Output should remain unchanged after this modification.
%% Cell type:markdown id: tags:
### EXERCICE 15: Add a method to give away the double value
Add in the struct a constant method `DoubleValue()` which computes the approximation value from both data attributes and returns it; use it in `display_power_of_2_approx()`.
Output should remain unchanged after this modification.
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Object programming](/notebooks/2-ObjectProgramming/0-main.ipynb) - [TP 5](/notebooks/2-ObjectProgramming/3b-TP.ipynb)
# [Getting started in C++](/) - [Object programming](/notebooks/2-ObjectProgramming/0-main.ipynb) - [TP 6](/notebooks/2-ObjectProgramming/3b-TP.ipynb)
%% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#EXERCICE-16:-change-interface-of-PowerOfTwoApprox-struct" data-toc-modified-id="EXERCICE-16:-change-interface-of-PowerOfTwoApprox-struct-1">EXERCICE 16: change interface of <code>PowerOfTwoApprox</code> struct</a></span></li></ul></div>
<div class="toc"><ul class="toc-item"><li><span><a href="#EXERCICE-15:-change-interface-of-PowerOfTwoApprox-struct" data-toc-modified-id="EXERCICE-15:-change-interface-of-PowerOfTwoApprox-struct-1">EXERCICE 15: change interface of <code>PowerOfTwoApprox</code> struct</a></span></li></ul></div>
%% Cell type:markdown id: tags:
### EXERCICE 16: change interface of `PowerOfTwoApprox` struct
### EXERCICE 15: change interface of `PowerOfTwoApprox` struct
* Transform `Compute()` into a constructor.
* Assign a default value of `0` to data attributes.
* Transform `Compute()` into a constructor. This constructor can't any longer retuen a double value; introduce a method `AsDouble()` which returns the approximation.
* Assign a default value of `0` to data attributes (if not already done previously!)
Expected output is the same as previously.
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Object programming](/notebooks/2-ObjectProgramming/0-main.ipynb) - [TP 6](/notebooks/2-ObjectProgramming/4b-TP.ipynb)
# [Getting started in C++](/) - [Object programming](/notebooks/2-ObjectProgramming/0-main.ipynb) - [TP 7](/notebooks/2-ObjectProgramming/4b-TP.ipynb)
%% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#EXERCICE-17:-transform-struct-PowerOfTwoApprox-into-a-class" data-toc-modified-id="EXERCICE-17:-transform-struct-PowerOfTwoApprox-into-a-class-1">EXERCICE 17: transform struct <code>PowerOfTwoApprox</code> into a class</a></span></li><li><span><a href="#EXERCICE-18:-transform-multiply()-into-a-method-Multiply()-of-PowerOfTwoApprox" data-toc-modified-id="EXERCICE-18:-transform-multiply()-into-a-method-Multiply()-of-PowerOfTwoApprox-2">EXERCICE 18: transform <code>multiply()</code> into a method <code>Multiply()</code> of <code>PowerOfTwoApprox</code></a></span></li><li><span><a href="#EXERCICE-19:-transform-display_power_of_2_approx()-into-a-class" data-toc-modified-id="EXERCICE-19:-transform-display_power_of_2_approx()-into-a-class-3">EXERCICE 19: transform <code>display_power_of_2_approx()</code> into a class</a></span></li><li><span><a href="#EXERCICE-20:-transform-display_sum()-into-a-class" data-toc-modified-id="EXERCICE-20:-transform-display_sum()-into-a-class-4">EXERCICE 20: transform <code>display_sum()</code> into a class</a></span></li><li><span><a href="#EXERCICE-21:-factorize-computation-and-display-of-error" data-toc-modified-id="EXERCICE-21:-factorize-computation-and-display-of-error-5">EXERCICE 21: factorize computation and display of error</a></span></li></ul></div>
<div class="toc"><ul class="toc-item"><li><span><a href="#EXERCICE-16:-transform-struct-PowerOfTwoApprox-into-a-class" data-toc-modified-id="EXERCICE-16:-transform-struct-PowerOfTwoApprox-into-a-class-1">EXERCICE 16: transform struct <code>PowerOfTwoApprox</code> into a class</a></span></li><li><span><a href="#EXERCICE-17:-transform-multiply()-into-a-method-Multiply()-of-PowerOfTwoApprox" data-toc-modified-id="EXERCICE-17:-transform-multiply()-into-a-method-Multiply()-of-PowerOfTwoApprox-2">EXERCICE 17: transform <code>multiply()</code> into a method <code>Multiply()</code> of <code>PowerOfTwoApprox</code></a></span></li><li><span><a href="#EXERCICE-18:-transform-display_power_of_2_approx()-into-a-class" data-toc-modified-id="EXERCICE-18:-transform-display_power_of_2_approx()-into-a-class-3">EXERCICE 18: transform <code>display_power_of_2_approx()</code> into a class</a></span></li><li><span><a href="#EXERCICE-19:-transform-display_multiply()-into-a-class" data-toc-modified-id="EXERCICE-19:-transform-display_multiply()-into-a-class-4">EXERCICE 19: transform <code>display_multiply()</code> into a class</a></span></li><li><span><a href="#EXERCICE-20:-introduce-common-print_line()-function-for-outputs" data-toc-modified-id="EXERCICE-20:-introduce-common-print_line()-function-for-outputs-5">EXERCICE 20: introduce common <code>print_line()</code> function for outputs</a></span></li></ul></div>
%% Cell type:markdown id: tags:
### EXERCICE 17: transform struct `PowerOfTwoApprox` into a class
### EXERCICE 16: transform struct `PowerOfTwoApprox` into a class
Make `PowerOfTwoApprox` into a class, with proper encapsulation:
* Both data attributes should be made private.
* Constant accessors will therefore be needed (non-constant ones should not be required here)
Expected output is the same as previously.
%% Cell type:markdown id: tags:
### EXERCICE 18: transform `multiply()` into a method `Multiply()` of `PowerOfTwoApprox`
### EXERCICE 17: transform `multiply()` into a method `Multiply()` of `PowerOfTwoApprox`
The method will take as argument only the integer coefficient.
`display_sum()` will of course need also some light rewriting to accomodate that change.
`display_multiply()` will of course need also some light rewriting to accomodate that change.
Expected output is the same as previously.
%% Cell type:markdown id: tags:
### EXERCICE 19: transform `display_power_of_2_approx()` into a class
### EXERCICE 18: transform `display_power_of_2_approx()` into a class
Create a class `TestDisplayPowerOfTwoApprox` which will be in charge of printing the display for the 0.65 and 0.35 values.
Use two methods in this class:
* A public method `Do()` which will call the test for 0.65 and 0.35; this method will take the number of bits as argument.
* A private method `Display()` which will provide the display for a given double value (and will therefore be called twice: once for 0.65 and once for 0.35).
New main should look like:
%% Cell type:code id: tags:
``` C++17
int main(int argc, char** argv)
{
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
TestDisplayPowerOfTwoApprox test_display_approx;
for (int Nbits = 2; Nbits <= 8; Nbits += 2)
test_display_approx.Do(Nbits);
std::cout << std::endl;
for (int Nbits = 1; Nbits <= 8; ++Nbits)
display_sum(Nbits, 0.65, 3515, 0.35, 4832);
display_multiply(Nbits, 0.65, 3515, 0.35, 4832);
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
Output will be ordered differently:
````
[With 2 bits]: 0.65 ~ 0.75 (3/2^2) [error = 15/100]
[With 2 bits]: 0.35 ~ 0.375 (3/2^3) [error = 7/100]
[With 4 bits]: 0.65 ~ 0.625 (10/2^4) [error = 4/100]
[With 4 bits]: 0.35 ~ 0.34375 (11/2^5) [error = 2/100]
[With 6 bits]: 0.65 ~ 0.65625 (42/2^6) [error = 1/100]
[With 6 bits]: 0.35 ~ 0.351562 (45/2^7) [error = 0/100]
[With 8 bits]: 0.65 ~ 0.648438 (166/2^8) [error = 0/100]
[With 8 bits]: 0.35 ~ 0.349609 (179/2^9) [error = 0/100]
[With 1 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 2965 [error = 254/1000]
[With 2 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4448 [error = 119/1000]
[With 3 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4008 [error = 8/1000]
[With 4 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3857 [error = 30/1000]
[With 5 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3967 [error = 2/1000]
[With 6 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4004 [error = 7/1000]
[With 7 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3977 [error = 0/1000]
[With 8 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3968 [error = 2/1000]
````
%% Cell type:markdown id: tags:
### EXERCICE 20: transform `display_sum()` into a class
### EXERCICE 19: transform `display_multiply()` into a class
Likewise, create a class `TestDisplaySum` which will be in charge of printing the display for 0.65 * 3515 + 0.35 * 4832 with public method `Do()` and private method `Display()`.
Likewise, create a class `TestDisplayMultiply` which will be in charge of printing the display for 0.65 * 3515 + 0.35 * 4832 with public method `Do()` and private method `Display()` which will takes 5 arguments:
* Number of bits.
* The two floating point values.
* Their integer coefficient.
New `main()` is:
%% Cell type:code id: tags:
``` C++17
int main(int argc, char** argv)
{
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
TestDisplayPowerOfTwoApprox test_display_approx;
for (int Nbits = 2; Nbits <= 8; Nbits += 2)
test_display_approx.Do(Nbits);
std::cout << std::endl;
TestDisplayMultiply test_display_multiply;
for (int Nbits = 1; Nbits <= 8; ++Nbits)
test_display_multiply.Do(Nbits);
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
### EXERCICE 21: factorize computation and display of error
### EXERCICE 20: introduce common `print_line()` function for outputs
Both `TestDisplay` classes are rather similar in the line in charge of printing content on standard output - so we would like to uniformize the implementation.
We will therefore introduce a `print_line()` function which will be in charge of printing the line for a given number of bits.
At the moment, we will renege the sound principle of separating the functionalities (humour me for the moment...) and this function will:
* Take as arguments the number of bits, the exact actual number (obtained through usual floating-point arithmetic) and the approximation of this number through our representation.
* Compute the error with a resolution also given as argument ((i.e. the maximum index against which error is expressed - 100 and 1000 respectively up to now).
Create the `error()` function that calculates and displays the error rate between a real number and its approximation, on an integer scale from 0 to a given maximum index. The input arguments are:
However there are subtleties in the way the lines are displayed: the parts in red is supplementary text that
* The number of bits used during the approximation
* The exact actual number
* The approximation of this number
* The resolution (the maximum index against which error is expressed - 100 and 1000 respectively up to now).
* Ideally strings to enable keeping the exact same output as previously; to achieve this you may need a reminder of [how to convert a number into a string](/notebooks/1-ProceduralProgramming/6-Streams.ipynb#Conversion) (the solution present one of the possible conversion but you may of course choose the other one as well!)
[With 2 bits]: 0.65 ~ 0.75 <font color="red">(3/2^2)</font> [error = 15/100]
This `error()` function should be called in both `Display()` methods.
[With 1 bits]: <font color="red">0.65 * 3515 + 0.35 * 4832 = </font> 3976 ~ 2965 [error = 254/1000]
So to do that you will need two strings arguments to provide the possibility to customize the line at the two locations pointed out in red (to achieve this you may need a reminder of [how to convert a number into a string](/notebooks/1-ProceduralProgramming/6-Streams.ipynb#Conversion)).
Last subtlety: for the `DisplayMultiply` case we round the exact value to an integer, but that would break the output for the `TestDisplayPowerOfTwoApprox` cases... So we introduce an enum class which will act as a boolean:
````
enum class RoundToInteger { no, yes };
````
To sum up, the signature or `print_line()` should be:
````
void print_line(int Nbits, double exact, double approx, int maximum_error_index,
std::string optional_string1, std::string optional_string2,
RoundToInteger round_to_integer);
````
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
This diff is collapsed.
......@@ -21,8 +21,3 @@ add_executable(initial initial_file.cpp)
# add_executable(exercice23 exercice23.cpp)
# add_executable(exercice24 exercice24.cpp)
# add_executable(exercice25 exercice25.cpp)
# add_executable(exercice26 exercice26.cpp)
# add_executable(exercice27 exercice27.cpp)
# add_executable(exercice28 exercice28.cpp)
# add_executable(exercice29 exercice29.cpp)
# add_executable(exercice30 exercice30.cpp)
\ No newline at end of file
<
#include <iostream>
#include <cmath> // for std::round
/************************************/
// Declarations
/************************************/
/*!
* \brief Structure to group together numerator and exponent used to represent a floating-point.
*
* Representation is numerator / 2^exponent, where numerator is as large as a selected number of bits make possible.
*/
struct PowerOfTwoApprox
{
//! Numerator.
int numerator_;
//! Exponent applied to the 2 at the denominator.
int exponent_;
};
//! Returns `number` * (2 ^ `exponent`)
int times_power_of_2(int number, int exponent);
//! Round to `x` the nearest integer.
int round_as_int(double x);
//! Display the approximation of `value` in the proposed representation with the highest possible
//! numerator that may be represented in `Nbits` bits.
void display_power_of_2_approx(int Nbits, double value);
//! Compute the best possible approximation of `value` with `Nbits`
//! \param[out] approx The `PowerOfTwoApprox` object filled appropriately.
void compute_power_of_2_approx(int Nbits, double value, PowerOfTwoApprox& approx);
/*!
* \brief Multiply an approximate representation of a double by an integer.
*
* \param[in] Nbits Number of bits available to represent `value` with the integer representation.
* \param[in] value Floating point value which is approximated by the integer representation.
* \param[in] coefficient Integer coefficient by which `value` is multiplied.
*
* \return An approximate integer result of the multiplication.
*/
int multiply(int Nbits, double value, int coefficient);
/*!
* \brief Display the approximate result of value1 * coefficient1 + value2 * coefficient2 computed with
* precision of `Nbits`.
*/
void display_sum(int Nbits, double value1, int coefficient1, double value2, int coefficient2);
// Maximum integer that might be represented with `Nbits` bits.
int max_int(int Nbits);
/************************************/
// Main function
/************************************/
int main(int argc, char** argv)
{
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
for (int Nbits = 2; Nbits <= 8; Nbits += 2)
display_power_of_2_approx(Nbits, 0.65) ;
std::cout << std::endl;
for (int Nbits = 2; Nbits <= 8; Nbits += 2)
display_power_of_2_approx(Nbits, 0.35) ;
std::cout << std::endl;
for (int Nbits = 1; Nbits <= 8; ++Nbits)
display_sum(Nbits, 0.65, 3515, 0.35, 4832);
return EXIT_SUCCESS;
}
/************************************/
// Definitions
/************************************/
int times_power_of_2(int number, int exponent)
{
while (exponent > 0)
......@@ -107,84 +18,131 @@ int times_power_of_2(int number, int exponent)
return number;
}
//! Round to `x` the nearest integer.
int round_as_int(double x)
{
return static_cast<int>(std::round(x));
}
void display_power_of_2_approx(int Nbits, double value)
// Maximum integer that might be represented with `Nbits` bits.
int max_int(int Nbits)
{
return (times_power_of_2(1, Nbits) - 1);
}
//! Structure to group the two integers used in the approximation we define.
struct PowerOfTwoApprox
{
PowerOfTwoApprox approx;
auto& numerator = approx.numerator_;
auto& exponent = approx.exponent_;
compute_power_of_2_approx(Nbits, value, approx);
int denominator = times_power_of_2(1, exponent);
double double_approx = static_cast<double>(numerator) / denominator;
int error = round_as_int(100. * std::fabs(value - double_approx) / value);
std::cout << "[With " << Nbits << " bits]: "
<< value << " ~ " << double_approx << " (" << numerator << "/2^" << exponent << ')'
<< " [error = " << error << "/" << "100]"
<< std::endl ;
int numerator_ {};
int exponent_ {};
};
//! Helper function that computes numerator and denominator.
//! You're not obliged to use a function, but this way you enforce the Don't Repeat Yourself (DRY) principle!
void helper_compute_power_of_2_approx(double value, int exponent, int& numerator, int& denominator)
{
denominator = times_power_of_2(1, exponent);
numerator = round_as_int(value * denominator);
}
void compute_power_of_2_approx(int Nbits, double value, PowerOfTwoApprox& approx)
//! Compute the best possible approximation of `value` with `Nbits`
//! \param[out] approximation Computed approximation.
//! \return The approximation as a floating point.
double compute_power_of_2_approx(int Nbits, double value, PowerOfTwoApprox& approximation)
{
auto& numerator = approx.numerator_;
auto& exponent = approx.exponent_;
numerator = exponent = 0;
int denominator {};
int& numerator = approximation.numerator_; // alias!
int& exponent = approximation.exponent_;
int max_numerator = max_int(Nbits);
numerator = 0;
exponent = 0;
int denominator {};
do
{
++exponent;
denominator = times_power_of_2(1, exponent);
numerator = round_as_int(value * denominator);
// I used here the preffix increment '++exponent' but you may put it on a separate line if you're not
// comfortable with it.
helper_compute_power_of_2_approx(value, ++exponent, numerator, denominator);
}
while (numerator <= max_numerator);
--exponent;
denominator = times_power_of_2(1, exponent);
numerator = round_as_int(value * denominator);
helper_compute_power_of_2_approx(value, --exponent, numerator, denominator);
return static_cast<double>(numerator) / denominator;
}
int multiply(int Nbits, double value, int coefficient)
//! Display the approximation of the given argument that fits in the given number of bits.
void display_power_of_2_approx(int Nbits, double value)
{
PowerOfTwoApprox approx;
compute_power_of_2_approx(Nbits, value, approx);
PowerOfTwoApprox approximation;
const double approx = compute_power_of_2_approx(Nbits, value, approximation);
return times_power_of_2(approx.numerator_ * coefficient, -approx.exponent_);
const double error = std::fabs(value - approx) / value;
std::cout << "[With " << Nbits << " bits]: " << value << " ~ " << approx << " (" << approximation.numerator_ <<
" / 2^" << approximation.exponent_ << ") [error = " << round_as_int(100. * error) << "/100]" << std::endl;
}
void display_sum(int Nbits, double value1, int coefficient1, double value2, int coefficient2)
//! Multiply an approximated value by an integer.
int multiply(int Nbits, double value, int coefficient)
{
PowerOfTwoApprox approximation;
compute_power_of_2_approx(Nbits, value, approximation);
return times_power_of_2(approximation.numerator_ * coefficient, -approximation.exponent_);
}
//! Compute value1 * coefficient1 + value2 * coefficient2 over Nbits bits.
void display_multiply(int Nbits, double value1, int coefficient1, double value2, int coefficient2)
{
double exact = value1 * coefficient1 + value2 * coefficient2;
int rounded = round_as_int(exact);
int approx = multiply(Nbits, value1, coefficient1) + multiply(Nbits, value2, coefficient2);
int error = round_as_int(1000. * std::fabs(exact - approx) / exact);
const double error = std::fabs(exact - approx) / exact;
std::cout << "[With " << Nbits << " bits]: " << value1 << " * " << coefficient1
<< " + " << value2 << " * " << coefficient2 << " = "
<< rounded << " ~ " << approx
<< " [error = " << error << "/" << "1000]"
<< std::endl;
<< " [error = " << round_as_int(1000. * error) << "/1000]" << std::endl;
}
int max_int(int Nbits)
{
return (times_power_of_2(1, Nbits) - 1);
/************************************/
// Main function
/************************************/
int main(int argc, char** argv)
{
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
for (int Nbits = 2; Nbits <= 8; Nbits += 2)
display_power_of_2_approx(Nbits, 0.65);
std::cout << std::endl;
for (int Nbits = 2; Nbits <= 8; Nbits += 2)
display_power_of_2_approx(Nbits, 0.35);