Commit 9da5bd90 authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Provide new solutions to template exercices.

parent ececfacb
......@@ -32,9 +32,9 @@
"source": [
"### EXERCICE 37: make `PowerOfTwoApprox` a template class\n",
"\n",
"The integer type used for the numerator_ attribute of the `PowerOfTwoApprox` class was chosen arbitrarily. Make it a class template parameter.\n",
"The integer type used for the `numerator_ `attribute of the `PowerOfTwoApprox` class was chosen arbitrarily. Make it a class template parameter.\n",
"\n",
"Only the numerator will be modified: the denominator and the number of bits remain `int`.\n",
"Only this attribute will be modified: the denominator and the number of bits remain `int`.\n",
"\n",
"Make sure to modify:\n",
"\n",
......@@ -81,14 +81,14 @@
"\n",
"To do so properly, you will have to make some classes and functions template. As you will see, doing such a change is rather cumbersome: it is better whenever possible to identify as soon as possible whether a template parameter is required or not.\n",
"\n",
"We will do so step by step, each exercice dealing with part of the changes.\n",
"We will do so step by step, each exercice dealing with part of the changes (this is \n",
"\n",
"In the first step, we will:\n",
"\n",
"* Modify `operator*` signature so that the return type and the argument use the template parameter rather than `int`.\n",
"* Modify both `operator*` signatures so that the return type and the argument use the template parameter rather than `int`.\n",
"* Make `TestDisplaySum` a template class as well: as it uses up directly `operator*`, you have to do it now.\n",
"\n",
"You should see warnings to solve... and disturbing runtime effects: the computation gets stuck (we were not very aware so far of overflow issues and we end up here with infinite or at least very long loops...)\n",
"**DON'T PANIC:** You should see warnings to solve... and disturbing runtime effects: the computation gets stuck (we were not very aware so far of overflow issues and we end up here with infinite or at least very long loops...)\n",
"\n",
"Don't get me wrong: the issue is not the templates themselves, but the fact our code is not robust enough and that we didn't see it previously as we worked on types and values (no more than 16 bits...) for which the potentiel problems were not appearant."
]
......@@ -105,10 +105,10 @@
"\n",
"To do so, a boolean argument should be added to:\n",
"\n",
"- `PowerOfTwoApprox` constructor [input/output]\n",
"- `TestDisplay::PrintNumericalError()` method [input]\n",
"- `times_power_of_2()` function [input/output]\n",
"- `max_int()` function [input/output]\n",
"- `PowerOfTwoApprox` constructor _[input/output]_\n",
"- `TestDisplay::PrintNumericalError()` method _[input]_\n",
"- `times_power_of_2()` function _[input/output]_\n",
"- `max_int()` function _[input/output]_\n",
"\n",
"that is set to true if an overflow occurred.\n",
"\n",
......@@ -140,6 +140,7 @@
"[With 4 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3857 [error = 29930/1000000]\n",
"[With 8 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3968 [error = 2012/1000000]\n",
"[With 12 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3975 [error = 252/1000000]\n",
"Overflow in times_power_of_2 with number = 1073741824 and exponent = 1\n",
"Overflow happened in PowerOfTwoApprox constructor for value 0.65 over 16 bits; everything is therefore set arbitrarily to zero at the moment.\n",
"Overflow happened in PowerOfTwoApprox constructor for value 0.35 over 16 bits; everything is therefore set arbitrarily to zero at the moment.\n",
"[With 16 bits]: Overflow!\n",
......@@ -170,28 +171,9 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[1minput_line_7:2:22: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1mno template named 'PowerOfTwoApprox'\u001b[0m\n",
"IntT operator*(const PowerOfTwoApprox<IntT>& approx, IntT coefficient)\n",
"\u001b[0;1;32m ^\n",
"\u001b[0m"
]
},
{
"ename": "Interpreter Error",
"evalue": "",
"output_type": "error",
"traceback": [
"Interpreter Error: "
]
}
],
"outputs": [],
"source": [
"// Doesn't run in Xeus-cling\n",
"\n",
......@@ -203,6 +185,34 @@
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you're doing such a cast though, you should end up with something like:\n",
"\n",
"````\n",
"[With 4 bits]: 0.65 ~ 0.625 (10/2^4) [error = 38462/1000000]\n",
"[With 8 bits]: 0.65 ~ 0.648438 (166/2^8) [error = 2404/1000000]\n",
"[With 12 bits]: 0.65 ~ 0.649902 (2662/2^12) [error = 150/1000000]\n",
"[With 16 bits]: 0.65 ~ 0.649994 (42598/2^16) [error = 9/1000000]\n",
"\n",
"[With 4 bits]: 0.35 ~ 0.34375 (11/2^5) [error = 17857/1000000]\n",
"[With 8 bits]: 0.35 ~ 0.349609 (179/2^9) [error = 1116/1000000]\n",
"[With 12 bits]: 0.35 ~ 0.349976 (2867/2^13) [error = 70/1000000]\n",
"[With 16 bits]: 0.35 ~ 0.349998 (45875/2^17) [error = 4/1000000]\n",
"\n",
"[With 4 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ -2286 [error = 1574950/1000000]\n",
"[With 8 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 1 [error = 999748/1000000]\n",
"[With 12 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 0 [error = 1000000/1000000]\n",
"Overflow in times_power_of_2 with number = 16384 and exponent = 2\n",
"Overflow happened in PowerOfTwoApprox constructor for value 0.65 over 16 bits; everything is therefore set arbitrarily to zero at the moment.\n",
"Overflow in times_power_of_2 with number = 16384 and exponent = 2\n",
"Overflow happened in PowerOfTwoApprox constructor for value 0.35 over 16 bits; everything is therefore set arbitrarily to zero at the moment.\n",
"[With 16 bits]: Overflow!\n",
"````"
]
},
{
"cell_type": "markdown",
"metadata": {},
......@@ -220,15 +230,15 @@
"The functions to use are:\n",
"\n",
"````\n",
"__builtin_add_overflow(T a, T b, T& sum)\n",
"__builtin_mul_overflow(T a, T b, T& product)\n",
"__builtin_add_overflow(T a, T b, T* sum)\n",
"__builtin_mul_overflow(T a, T b, T* product)\n",
"````\n",
"\n",
"where T is the type considered; these functions return 0 if no overflow occurs (and result is in this case given in third argument).\n",
"\n",
"Use them in `TestDisplaySum<IntT>::Display()` and `operator*(const PowerOfTwoApprox<IntT>& approx, IntT coefficient)` to control the arithmetic operations there.\n",
"\n",
"Expected result is:\n",
"Expected result is something like:\n",
"\n",
"````\n",
"[With 4 bits]: 0.65 ~ 0.625 (10/2^4) [error = 38462/1000000]\n",
......@@ -241,21 +251,27 @@
"[With 12 bits]: 0.35 ~ 0.349976 (2867/2^13) [error = 70/1000000]\n",
"[With 16 bits]: 0.35 ~ 0.349998 (45875/2^17) [error = 4/1000000]\n",
"\n",
"Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary max value will therefore be returned.\n",
"Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary max value will therefore be returned.\n",
"Overflow in estDisplaySum<IntT>::Display in sum.\n",
"Overflow in 3515 * 10\n",
"Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary value MAX_INT will therefore be returned.\n",
"Overflow in 4832 * 11\n",
"Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary value MAX_INT will therefore be returned.\n",
"Overflow in 3515 * 10/2^4 + 4832 * 11/2^5\n",
"[With 4 bits]: Overflow!\n",
"Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary max value will therefore be returned.\n",
"Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary max value will therefore be returned.\n",
"Overflow in estDisplaySum<IntT>::Display in sum.\n",
"Overflow in 3515 * 166\n",
"Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary value MAX_INT will therefore be returned.\n",
"Overflow in 4832 * 179\n",
"Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary value MAX_INT will therefore be returned.\n",
"Overflow in 3515 * 166/2^8 + 4832 * 179/2^9\n",
"[With 8 bits]: Overflow!\n",
"Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary max value will therefore be returned.\n",
"Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary max value will therefore be returned.\n",
"Overflow in estDisplaySum<IntT>::Display in sum.\n",
"Overflow in 3515 * 2662\n",
"Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary value MAX_INT will therefore be returned.\n",
"Overflow in 4832 * 2867\n",
"Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary value MAX_INT will therefore be returned.\n",
"Overflow in 3515 * 2662/2^12 + 4832 * 2867/2^13\n",
"[With 12 bits]: Overflow!\n",
"Overflow in times_power_of_2!\n",
"Overflow in times_power_of_2 with number = 16384 and exponent = 2\n",
"Overflow happened in PowerOfTwoApprox constructor for value 0.65 over 16 bits; everything is therefore set arbitrarily to zero at the moment.\n",
"Overflow in times_power_of_2!\n",
"Overflow in times_power_of_2 with number = 16384 and exponent = 2\n",
"Overflow happened in PowerOfTwoApprox constructor for value 0.35 over 16 bits; everything is therefore set arbitrarily to zero at the moment.\n",
"[With 16 bits]: Overflow!\n",
"````\n"
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Templates](/notebooks/4-Templates/0-main.ipynb) - [TP 11](/notebooks/4-Templates/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-37:-make-PowerOfTwoApprox-a-template-class" data-toc-modified-id="EXERCICE-37:-make-PowerOfTwoApprox-a-template-class-2">EXERCICE 37: make <code>PowerOfTwoApprox</code> a template class</a></span></li><li><span><a href="#EXERCICE-38:-consistency" data-toc-modified-id="EXERCICE-38:-consistency-3">EXERCICE 38: consistency</a></span></li><li><span><a href="#EXERCICE-39:-crude-error-handling-for-the-overflow-issue" data-toc-modified-id="EXERCICE-39:-crude-error-handling-for-the-overflow-issue-4">EXERCICE 39: crude error handling for the overflow issue</a></span></li><li><span><a href="#EXERCICE-40:-more-template-functions" data-toc-modified-id="EXERCICE-40:-more-template-functions-5">EXERCICE 40: more template functions</a></span></li><li><span><a href="#EXERCICE-41:-another-error-handling..." data-toc-modified-id="EXERCICE-41:-another-error-handling...-6">EXERCICE 41: another error handling...</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.
%% Cell type:markdown id: tags:
### EXERCICE 37: make `PowerOfTwoApprox` a template class
The integer type used for the numerator_ attribute of the `PowerOfTwoApprox` class was chosen arbitrarily. Make it a class template parameter.
The integer type used for the `numerator_ `attribute of the `PowerOfTwoApprox` class was chosen arbitrarily. Make it a class template parameter.
Only the numerator will be modified: the denominator and the number of bits remain `int`.
Only this attribute will be modified: the denominator and the number of bits remain `int`.
Make sure to modify:
* The type of the data attribute.
* The type returned by the `Numerator()` method.
In the `TestDisplay` classes, we will for the moment keep instantiating the version with `int`.
**IMPORTANT:** For the friendship declaration in `PowerOfTwoApprox` to `operator*`, you need to specify another template for the function: you can't use the same as the class one:
````
template<class T>
class Foo
{
friend void MyFunction<Foo<T>>; // doesn't do what you really want!
};
````
````
template<class T>
class Foo
{
template<class U>
friend void MyFunction<Foo<U>>; // Ok!
};
````
%% Cell type:markdown id: tags:
### EXERCICE 38: consistency
So far, we aren't very self-consistent: we loosen the constraint on the type used to store the numerator, but it is used up against `int` elsewhere.
To highlight this, change `PowerOfTwoApprox` instantiation by replacing `int` by `short`: you should see warnings pop up...
To do so properly, you will have to make some classes and functions template. As you will see, doing such a change is rather cumbersome: it is better whenever possible to identify as soon as possible whether a template parameter is required or not.
We will do so step by step, each exercice dealing with part of the changes.
We will do so step by step, each exercice dealing with part of the changes (this is
In the first step, we will:
* Modify `operator*` signature so that the return type and the argument use the template parameter rather than `int`.
* Modify both `operator*` signatures so that the return type and the argument use the template parameter rather than `int`.
* Make `TestDisplaySum` a template class as well: as it uses up directly `operator*`, you have to do it now.
You should see warnings to solve... and disturbing runtime effects: the computation gets stuck (we were not very aware so far of overflow issues and we end up here with infinite or at least very long loops...)
**DON'T PANIC:** You should see warnings to solve... and disturbing runtime effects: the computation gets stuck (we were not very aware so far of overflow issues and we end up here with infinite or at least very long loops...)
Don't get me wrong: the issue is not the templates themselves, but the fact our code is not robust enough and that we didn't see it previously as we worked on types and values (no more than 16 bits...) for which the potentiel problems were not appearant.
%% Cell type:markdown id: tags:
### EXERCICE 39: crude error handling for the overflow issue
This exercice is not template-related, but as often in software programming, we have obviously to deal swiftly with the unforeseen issue at hand so that we can keep working on what we intended to implement in the first place.
We will provide several fixes: we want to be able to track down when overflow occurs, and be able to print with `PrintNumericalError()` method that an overflow happens rather than a garbage value.
To do so, a boolean argument should be added to:
- `PowerOfTwoApprox` constructor [input/output]
- `TestDisplay::PrintNumericalError()` method [input]
- `times_power_of_2()` function [input/output]
- `max_int()` function [input/output]
- `PowerOfTwoApprox` constructor _[input/output]_
- `TestDisplay::PrintNumericalError()` method _[input]_
- `times_power_of_2()` function _[input/output]_
- `max_int()` function _[input/output]_
that is set to true if an overflow occurred.
The two overflows to track down are:
- In `PowerOfTwoApprox` constructor:
If in the loop the numerator or the denominator is less than the one computed at the previous iteration, an overflow occurred. Implement a test and when this happens, exit the loop, print a message about the issue and set arbitrarily numerator and exponent to 0
- In `times_power_of_2`:
Before multiplying by 2., check number is less than half the maximum possible value for the type (use `std::numeric_limits<>::max()`).
We'll see [later](/notebooks/5-UsefulConceptsAndSTL/1-ErrorHandling.ipynb) how to deal more cleanly with error handlings.
The output should look something like:
````
[With 4 bits]: 0.65 ~ 0.625 (10/2^4) [error = 38462/1000000]
[With 8 bits]: 0.65 ~ 0.648438 (166/2^8) [error = 2404/1000000]
[With 12 bits]: 0.65 ~ 0.649902 (2662/2^12) [error = 150/1000000]
[With 16 bits]: 0.65 ~ 0.649994 (42598/2^16) [error = 9/1000000]
[With 4 bits]: 0.35 ~ 0.34375 (11/2^5) [error = 17857/1000000]
[With 8 bits]: 0.35 ~ 0.349609 (179/2^9) [error = 1116/1000000]
[With 12 bits]: 0.35 ~ 0.349976 (2867/2^13) [error = 70/1000000]
[With 16 bits]: 0.35 ~ 0.349998 (45875/2^17) [error = 4/1000000]
[With 4 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3857 [error = 29930/1000000]
[With 8 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3968 [error = 2012/1000000]
[With 12 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3975 [error = 252/1000000]
Overflow in times_power_of_2 with number = 1073741824 and exponent = 1
Overflow happened in PowerOfTwoApprox constructor for value 0.65 over 16 bits; everything is therefore set arbitrarily to zero at the moment.
Overflow happened in PowerOfTwoApprox constructor for value 0.35 over 16 bits; everything is therefore set arbitrarily to zero at the moment.
[With 16 bits]: Overflow!
````
%% Cell type:markdown id: tags:
### EXERCICE 40: more template functions
We will first heed the warnings: there is a type inconsistency due to the fact this function expects `int` and is fed `short`. Make some functions template ones to fix some of the warnings: `times_power_of_2`, `max_int` and `round_as_int`.
**WARNING:** You may have a hard time fixing the one concerning `operator*` due to a pesky
````
warning: implicit conversion loses
integer precision: 'int' to 'short' [-Wconversion]
return times_power_of_2(approx.Numerator() * coefficient, -approx.Exponent());
````
This is due to a specificity I was not aware of before preparing this very TP: an arithmetic operator involving two `short` actually returns an `int`! For more of this, see this [StackOverflow question](https://stackoverflow.com/questions/24371868/why-must-a-short-be-converted-to-an-int-before-arithmetic-operations-in-c-and-c). I actually wasn't aware of this because I never use `short`: I was told long ago it was often a pessimization compared to a mere `int` and didn't dig deeper than that; the issue we met here seems to confirm it is quirky to use.
One can fix the warning with an additional `static_cast`:
%% Cell type:code id: tags:
``` C++17
// Doesn't run in Xeus-cling
template<class IntT>
IntT operator*(const PowerOfTwoApprox<IntT>& approx, IntT coefficient)
{
// static_cast to deal with the `short` case
return static_cast<IntT>(times_power_of_2(approx.Numerator() * coefficient, -approx.Exponent()));
}
```
%%%% Output: stream
%% Cell type:markdown id: tags:
input_line_7:2:22: error: no template named 'PowerOfTwoApprox'
IntT operator*(const PowerOfTwoApprox<IntT>& approx, IntT coefficient)
 ^

If you're doing such a cast though, you should end up with something like:
%%%% Output: error
````
[With 4 bits]: 0.65 ~ 0.625 (10/2^4) [error = 38462/1000000]
[With 8 bits]: 0.65 ~ 0.648438 (166/2^8) [error = 2404/1000000]
[With 12 bits]: 0.65 ~ 0.649902 (2662/2^12) [error = 150/1000000]
[With 16 bits]: 0.65 ~ 0.649994 (42598/2^16) [error = 9/1000000]
Interpreter Error:
[With 4 bits]: 0.35 ~ 0.34375 (11/2^5) [error = 17857/1000000]
[With 8 bits]: 0.35 ~ 0.349609 (179/2^9) [error = 1116/1000000]
[With 12 bits]: 0.35 ~ 0.349976 (2867/2^13) [error = 70/1000000]
[With 16 bits]: 0.35 ~ 0.349998 (45875/2^17) [error = 4/1000000]
[With 4 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ -2286 [error = 1574950/1000000]
[With 8 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 1 [error = 999748/1000000]
[With 12 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 0 [error = 1000000/1000000]
Overflow in times_power_of_2 with number = 16384 and exponent = 2
Overflow happened in PowerOfTwoApprox constructor for value 0.65 over 16 bits; everything is therefore set arbitrarily to zero at the moment.
Overflow in times_power_of_2 with number = 16384 and exponent = 2
Overflow happened in PowerOfTwoApprox constructor for value 0.35 over 16 bits; everything is therefore set arbitrarily to zero at the moment.
[With 16 bits]: Overflow!
````
%% Cell type:markdown id: tags:
### EXERCICE 41: another error handling...
_Optional: you may want to take the solution directly!_
Previous exercice should also underline another limitation: we may detect the errors in building a `PowerOfTwoApprox`, but all bets are off when we perform arithmetics... Once again templates aren't the cause of our issue, it's just that the problem was hidden with the `int` used beforehand.
The issue we have now is that in plain arithmetic operations between `short`, we may overflow, especially considering our test example in which coefficients are already in the thousands.
This [StackOverflow post](https://stackoverflow.com/questions/199333/how-to-detect-unsigned-integer-multiply-overflow) provides an interesting non-standard feature introduced in clang and gcc to deal with this: special functions that tell if an operation will overflow (other replies give hints to do so manually).
The functions to use are:
````
__builtin_add_overflow(T a, T b, T& sum)
__builtin_mul_overflow(T a, T b, T& product)
__builtin_add_overflow(T a, T b, T* sum)
__builtin_mul_overflow(T a, T b, T* product)
````
where T is the type considered; these functions return 0 if no overflow occurs (and result is in this case given in third argument).
Use them in `TestDisplaySum<IntT>::Display()` and `operator*(const PowerOfTwoApprox<IntT>& approx, IntT coefficient)` to control the arithmetic operations there.
Expected result is:
Expected result is something like:
````
[With 4 bits]: 0.65 ~ 0.625 (10/2^4) [error = 38462/1000000]
[With 8 bits]: 0.65 ~ 0.648438 (166/2^8) [error = 2404/1000000]
[With 12 bits]: 0.65 ~ 0.649902 (2662/2^12) [error = 150/1000000]
[With 16 bits]: 0.65 ~ 0.649994 (42598/2^16) [error = 9/1000000]
[With 4 bits]: 0.35 ~ 0.34375 (11/2^5) [error = 17857/1000000]
[With 8 bits]: 0.35 ~ 0.349609 (179/2^9) [error = 1116/1000000]
[With 12 bits]: 0.35 ~ 0.349976 (2867/2^13) [error = 70/1000000]
[With 16 bits]: 0.35 ~ 0.349998 (45875/2^17) [error = 4/1000000]
Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary max value will therefore be returned.
Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary max value will therefore be returned.
Overflow in estDisplaySum<IntT>::Display in sum.
Overflow in 3515 * 10
Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary value MAX_INT will therefore be returned.
Overflow in 4832 * 11
Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary value MAX_INT will therefore be returned.
Overflow in 3515 * 10/2^4 + 4832 * 11/2^5
[With 4 bits]: Overflow!
Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary max value will therefore be returned.
Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary max value will therefore be returned.
Overflow in estDisplaySum<IntT>::Display in sum.
Overflow in 3515 * 166
Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary value MAX_INT will therefore be returned.
Overflow in 4832 * 179
Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary value MAX_INT will therefore be returned.
Overflow in 3515 * 166/2^8 + 4832 * 179/2^9
[With 8 bits]: Overflow!
Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary max value will therefore be returned.
Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary max value will therefore be returned.
Overflow in estDisplaySum<IntT>::Display in sum.
Overflow in 3515 * 2662
Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary value MAX_INT will therefore be returned.
Overflow in 4832 * 2867
Overflow in operator*(PowerOfTwoApprox, integer)! Arbitrary value MAX_INT will therefore be returned.
Overflow in 3515 * 2662/2^12 + 4832 * 2867/2^13
[With 12 bits]: Overflow!
Overflow in times_power_of_2!
Overflow in times_power_of_2 with number = 16384 and exponent = 2
Overflow happened in PowerOfTwoApprox constructor for value 0.65 over 16 bits; everything is therefore set arbitrarily to zero at the moment.
Overflow in times_power_of_2!
Overflow in times_power_of_2 with number = 16384 and exponent = 2
Overflow happened in PowerOfTwoApprox constructor for value 0.35 over 16 bits; everything is therefore set arbitrarily to zero at the moment.
[With 16 bits]: Overflow!
````
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_
_This notebook is an adaptation of a lecture prepared and redacted 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 redacted by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
......@@ -14,7 +14,7 @@
},
"source": [
"<h1>Table of contents<span class=\"tocSkip\"></span></h1>\n",
"<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#EXERCICE-42:-make-TestDisplayPowerOfTwoApprox-a-template-class\" data-toc-modified-id=\"EXERCICE-42:-make-TestDisplayPowerOfTwoApprox-a-template-class-1\">EXERCICE 42: make <code>TestDisplayPowerOfTwoApprox</code> a template class</a></span></li><li><span><a href=\"#EXERCICE-43:-char-display\" data-toc-modified-id=\"EXERCICE-43:-char-display-2\">EXERCICE 43: <code>char</code> display</a></span></li><li><span><a href=\"#EXERCICE-44:-integer-template-argument\" data-toc-modified-id=\"EXERCICE-44:-integer-template-argument-3\">EXERCICE 44: integer template argument</a></span></li></ul></div>"
"<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#EXERCICE-42:-make-TestDisplayPowerOfTwoApprox-a-template-class\" data-toc-modified-id=\"EXERCICE-42:-make-TestDisplayPowerOfTwoApprox-a-template-class-1\">EXERCICE 42: make <code>TestDisplayPowerOfTwoApprox</code> a template class</a></span></li><li><span><a href=\"#[optional]-EXERCICE-43:-char-display\" data-toc-modified-id=\"[optional]-EXERCICE-43:-char-display-2\">[optional] EXERCICE 43: <code>char</code> display</a></span></li><li><span><a href=\"#EXERCICE-44:-integer-template-argument\" data-toc-modified-id=\"EXERCICE-44:-integer-template-argument-3\">EXERCICE 44: integer template argument</a></span></li></ul></div>"
]
},
{
......@@ -23,9 +23,47 @@
"source": [
"### EXERCICE 42: make `TestDisplayPowerOfTwoApprox` a template class\n",
"\n",
"Now we will make `TestDisplayPowerOfTwo` (and therefore its derived classes) a template class. Don't forget to modify `TestDisplayPowerOfTwoApprox<IntT>::Display()` and make the local `PowerOfTwoApprox` object the templated type!\n",
"Now we will make `TestDisplayPowerOfTwoApprox` (and therefore its derived classes) a template class. Don't forget to modify `TestDisplayPowerOfTwoApprox<IntT>::Display()` and make the local `PowerOfTwoApprox` object the templated type!\n",
"\n",
"In the derived classes, you will need to help the compiler to figure out inheritance is at play, with `this` or an appropriate alias.\n",
"\n",
"Try it with the following `main()`:\n",
"\n",
"````\n",
"int main(int argc, char** argv)\n",
"{\n",
" static_cast<void>(argc); // to silence warning about unused argc - don't bother \n",
" static_cast<void>(argv); // to silence warning about unused argv - don't bother \n",
" \n",
" TestDisplayContainer container(3);\n",
" container.Register(new TestDisplayPowerOfTwoApprox065<int>(1000000));\n",
" container.Register(new TestDisplayPowerOfTwoApprox035<int>(1000000));\n",
" container.Register(new TestDisplaySum<int>(1000000));\n",
" \n",
" loop(4, 16, 4, container);\n",
"\n",
" return EXIT_SUCCESS;\n",
"}\n",
"````\n",
"\n",
"You should get:\n",
"\n",
"````\n",
"[With 4 bits]: 0.65 ~ 0.625 (10/2^4) [error = 38462/1000000]\n",
"[With 8 bits]: 0.65 ~ 0.648438 (166/2^8) [error = 2404/1000000]\n",
"[With 12 bits]: 0.65 ~ 0.649902 (2662/2^12) [error = 150/1000000]\n",
"[With 16 bits]: 0.65 ~ 0.649994 (42598/2^16) [error = 9/1000000]\n",
"\n",
"[With 4 bits]: 0.35 ~ 0.34375 (11/2^5) [error = 17857/1000000]\n",
"[With 8 bits]: 0.35 ~ 0.349609 (179/2^9) [error = 1116/1000000]\n",
"[With 12 bits]: 0.35 ~ 0.349976 (2867/2^13) [error = 70/1000000]\n",
"[With 16 bits]: 0.35 ~ 0.349998 (45875/2^17) [error = 4/1000000]\n",
"\n",
"[With 4 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3857 [error = 29930/1000000]\n",
"[With 8 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3968 [error = 2012/1000000]\n",
"[With 12 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3975 [error = 252/1000000]\n",
"[With 16 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3975 [error = 252/1000000]\n",
"````\n",
"\n"
]
},
......@@ -33,14 +71,11 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 43: `char` display\n",
"\n",
"I'm not sure you would be willing to given the work `short` already gave use, but you may use an even shorter type to represent an integer: `char`!\n",
"### [optional] EXERCICE 43: `char` display\n",
"\n",
"Replace the `main` by the following:\n",
"I'm not sure you would be willing to try it given the work `short` already gave, but you may use an even shorter type to represent an integer: `char`!\n",
"\n",
"\n",
"\n"
"Replace the `main` by the following:\n"
]
},
{
......@@ -60,7 +95,7 @@
" TestDisplayContainer container(4);\n",
" container.Register(new TestDisplayPowerOfTwoApprox065<int>(1000000));\n",
" container.Register(new TestDisplayPowerOfTwoApprox035<int>(1000000));\n",
" container.Register(new TestDisplaySum<short>(1000000));\n",
" container.Register(new TestDisplaySum<int>(1000000));\n",
" \n",
" loop(4, 16, 4, container);\n",
" }\n",
......@@ -80,10 +115,13 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"What do you observe? Can you provide a patch to fix this?\n",
"You should observe gibberish like:\n",
"\n",
"**NOTE:** Two solutions are provided for this one: the C++ 17 one, and the more verbosy one compatible with former standards. The next solutions will take the less elegant one to ensure more compatibility.\n",
"\n"
"````\n",
"Overflow in times_power_of_2 with number = @ and exponent = 1\n",
"````\n",
"\n",
"Patch the outputs so that char integers are properly printed as numbers."
]
},
{
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Templates](/notebooks/4-Templates/0-main.ipynb) - [TP 12](/notebooks/4-Templates/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-42:-make-TestDisplayPowerOfTwoApprox-a-template-class" data-toc-modified-id="EXERCICE-42:-make-TestDisplayPowerOfTwoApprox-a-template-class-1">EXERCICE 42: make <code>TestDisplayPowerOfTwoApprox</code> a template class</a></span></li><li><span><a href="#EXERCICE-43:-char-display" data-toc-modified-id="EXERCICE-43:-char-display-2">EXERCICE 43: <code>char</code> display</a></span></li><li><span><a href="#EXERCICE-44:-integer-template-argument" data-toc-modified-id="EXERCICE-44:-integer-template-argument-3">EXERCICE 44: integer template argument</a></span></li></ul></div>
<div class="toc"><ul class="toc-item"><li><span><a href="#EXERCICE-42:-make-TestDisplayPowerOfTwoApprox-a-template-class" data-toc-modified-id="EXERCICE-42:-make-TestDisplayPowerOfTwoApprox-a-template-class-1">EXERCICE 42: make <code>TestDisplayPowerOfTwoApprox</code> a template class</a></span></li><li><span><a href="#[optional]-EXERCICE-43:-char-display" data-toc-modified-id="[optional]-EXERCICE-43:-char-display-2">[optional] EXERCICE 43: <code>char</code> display</a></span></li><li><span><a href="#EXERCICE-44:-integer-template-argument" data-toc-modified-id="EXERCICE-44:-integer-template-argument-3">EXERCICE 44: integer template argument</a></span></li></ul></div>
%% Cell type:markdown id: tags:
### EXERCICE 42: make `TestDisplayPowerOfTwoApprox` a template class
Now we will make `TestDisplayPowerOfTwo` (and therefore its derived classes) a template class. Don't forget to modify `TestDisplayPowerOfTwoApprox<IntT>::Display()` and make the local `PowerOfTwoApprox` object the templated type!
Now we will make `TestDisplayPowerOfTwoApprox` (and therefore its derived classes) a template class. Don't forget to modify `TestDisplayPowerOfTwoApprox<IntT>::Display()` and make the local `PowerOfTwoApprox` object the templated type!
In the derived classes, you will need to help the compiler to figure out inheritance is at play, with `this` or an appropriate alias.
Try it with the following `main()`:
%% Cell type:markdown id: tags:
````
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
### EXERCICE 43: `char` display
TestDisplayContainer container(3);
container.Register(new TestDisplayPowerOfTwoApprox065<int>(1000000));
container.Register(new TestDisplayPowerOfTwoApprox035<int>(1000000));
container.Register(new TestDisplaySum<int>(1000000));
I'm not sure you would be willing to given the work `short` already gave use, but you may use an even shorter type to represent an integer: `char`!
loop(4, 16, 4, container);
Replace the `main` by the following:
return EXIT_SUCCESS;
}
````
You should get:
````
[With 4 bits]: 0.65 ~ 0.625 (10/2^4) [error = 38462/1000000]
[With 8 bits]: 0.65 ~ 0.648438 (166/2^8) [error = 2404/1000000]
[With 12 bits]: 0.65 ~ 0.649902 (2662/2^12) [error = 150/1000000]
[With 16 bits]: 0.65 ~ 0.649994 (42598/2^16) [error = 9/1000000]
[With 4 bits]: 0.35 ~ 0.34375 (11/2^5) [error = 17857/1000000]
[With 8 bits]: 0.35 ~ 0.349609 (179/2^9) [error = 1116/1000000]
[With 12 bits]: 0.35 ~ 0.349976 (2867/2^13) [error = 70/1000000]
[With 16 bits]: 0.35 ~ 0.349998 (45875/2^17) [error = 4/1000000]
[With 4 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3857 [error = 29930/1000000]
[With 8 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3968 [error = 2012/1000000]
[With 12 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3975 [error = 252/1000000]
[With 16 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3975 [error = 252/1000000]
````
%% Cell type:markdown id: tags:
### [optional] EXERCICE 43: `char` display
I'm not sure you would be willing to try it given the work `short` already gave, but you may use an even shorter type to represent an integer: `char`!
Replace the `main` by the following:
%% Cell type:code id: tags:
``` C++17
// Not executable as a Xeus-cling cell!
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
{
TestDisplayContainer container(4);
container.Register(new TestDisplayPowerOfTwoApprox065<int>(1000000));
container.Register(new TestDisplayPowerOfTwoApprox035<int>(1000000));
container.Register(new TestDisplaySum<short>(1000000));
container.Register(new TestDisplaySum<int>(1000000));
loop(4, 16, 4, container);
}
{
TestDisplayContainer container(2);
container.Register(new TestDisplayPowerOfTwoApprox065<char>(1000));
container.Register(new TestDisplayPowerOfTwoApprox065<unsigned char>(1000));
loop(1, 8, 1, container);
}
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
What do you observe? Can you provide a patch to fix this?
You should observe gibberish like:
**NOTE:** Two solutions are provided for this one: the C++ 17 one, and the more verbosy one compatible with former standards. The next solutions will take the less elegant one to ensure more compatibility.
````
Overflow in times_power_of_2 with number = @ and exponent = 1
````
Patch the outputs so that char integers are properly printed as numbers.
%% Cell type:markdown id: tags:
### EXERCICE 44: integer template argument
Make `TestDisplayContainer` a template class which template argument is an integer `SizeN` that specifies the number of elements that might be stored inside (data attribute `size_` is of course no longer required!)
There are several school of thoughts about the type you should give this integer:
* The inspiration for this lecture advised strongly to make it an `int`, to avoid conversions when the size was compared with other quantities that are represented by an `int`. You should in this case add a `static_assert` within the class declaration to ensure the quantity is positive:
````
static_assert(SizeN > 0)
````
* You may prefer to use an `unsigned int` - personally I like to use this type when a quantity has no business being negative.
* You may even prefer `std::size_t`, which is for more distribution an alias for `unsigned long`. This might seem overkill, but it is the choice in the standard library containers.
In the proposed solution I will choose the first solution to illustrate `static_assert`, but in a true project I would probably take the third one.
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_
_This notebook is an adaptation of a lecture prepared and redacted 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 redacted by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
......@@ -41,13 +41,11 @@ add_cxx_compiler_flag("-Wno-padded")
add_executable(initial initial_file.cpp)
# add_executable(exercice37 exercice37.cpp)
# add_executable(exercice38 exercice38.cpp)
# add_executable(exercice39 exercice39.cpp)
# add_executable(exercice39_bis exercice39_bis.cpp)
#add_executable(exercice37 exercice37.cpp)
#add_executable(exercice38 exercice38.cpp)
#add_executable(exercice39 exercice39.cpp)
# add_executable(exercice40 exercice40.cpp)
# add_executable(exercice41 exercice41.cpp)
# add_executable(exercice42 exercice42.cpp)
# add_executable(exercice43_constexpr exercice43_constexpr.cpp)
# add_executable(exercice43_specialization exercice43_specialization.cpp)
# add_executable(exercice43 exercice43.cpp)
# add_executable(exercice44 exercice44.cpp)
\ No newline at end of file
......@@ -4,8 +4,45 @@
#include <sstream> // for std::ostringstream
//! Returns `number` * (2 ^ `exponent`)
int times_power_of_2(int number, int exponent)
{
while (exponent > 0)
{
number *= 2;
exponent -= 1;
}
while (exponent < 0)
{
number /= 2;
exponent += 1 ;
}
return number;
}
//! Round to `x` the nearest integer.
int round_as_int(double x)
{
return static_cast<int>(std::round(x));
}
//! Maximum integer that might be represented with `Nbits` bits.
int max_int(int Nbits)
{
return (times_power_of_2(1, Nbits) - 1);
}
//! Function to call when an error occurred; its aborts the program.
[[noreturn]] void error(std::string explanation)
{
std::cout << "ERROR: " << explanation << std::endl;
exit(EXIT_FAILURE);
}
/************************************/
// Declarations
// PowerOfTwoApprox
/************************************/
/*!
......@@ -32,8 +69,8 @@ public:
*/
template<class IntU>
friend int operator*(const PowerOfTwoApprox<IntU>& approx, int coefficient);
template<class IntU>
template<class IntU>
friend int operator*(int coefficient, const PowerOfTwoApprox<IntU>& approx);
//! Get the value of the numerator.
......@@ -52,17 +89,86 @@ private:
};
template<class IntT>
PowerOfTwoApprox<IntT>::PowerOfTwoApprox(int Nbits, double value)
{
auto& numerator = numerator_;
auto& exponent = exponent_;
numerator = exponent = 0;
int denominator {};
int max_numerator = max_int(Nbits);
do
{
++exponent;
denominator = times_power_of_2(1, exponent);
numerator = round_as_int(value * denominator);
}