Commit 1733371f authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Procedural programming: add the TP notebooks in the main and reindex so that...

Procedural programming: add the TP notebooks in the main and reindex so that all TPs follow the same convention.
parent 47c2367e
......@@ -161,7 +161,7 @@
"\n",
"You may have notices the braces `{` and `}` in the examples above. They are here a technicality to make cling interpreter work better in a Jupyter environment, but that is also useful in a real C++ code.\n",
"\n",
"What is between both braces constitute a **block**; variables that are initialized inside are destroyed once the closing brace is past (don't worry - we will come back to that [later](/notebooks/1-ProceduralProgramming/6-DynamicAllocation.ipynb#Stack)).\n",
"What is between both braces constitute a **block**; variables that are initialized inside are destroyed once the closing brace is past (don't worry - we will come back to that [later](/notebooks/1-ProceduralProgramming/5-DynamicAllocation.ipynb#Stack)).\n",
"\n",
"This is an incredibly useful feature: you may ensure this way that a variable is not kept any longer than necessary. We will come back to this feature (called the **locality of reference** later); let's see why they are useful in a notebook environment.\n",
"\n",
......@@ -308,7 +308,7 @@
"\n",
"The operator `<<` is used to indicate what you direct toward the stream; here std::cout << \"Hello world!\" tells to redirect the string toward the standard output.\n",
"\n",
"We will see that a bit more in detail in [a later chapter](/notebooks/1-ProceduralProgramming/7-Streams.ipynb), but printing something is really helpful early on hence this brief introduction here.\n"
"We will see that a bit more in detail in [a later chapter](/notebooks/1-ProceduralProgramming/6-Streams.ipynb), but printing something is really helpful early on hence this brief introduction here.\n"
]
},
{
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Getting started with the tutorial](/notebooks/getting_started_with_tutorial.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="#About-the-choice-of-a-Jupyter-notebook" data-toc-modified-id="About-the-choice-of-a-Jupyter-notebook-1">About the choice of a Jupyter notebook</a></span></li><li><span><a href="#When-the-notebook-is-not-enough..." data-toc-modified-id="When-the-notebook-is-not-enough...-2">When the notebook is not enough...</a></span></li><li><span><a href="#Few-guidelines-about-Jupyter" data-toc-modified-id="Few-guidelines-about-Jupyter-3">Few guidelines about Jupyter</a></span><ul class="toc-item"><li><span><a href="#Restarting-the-kernel" data-toc-modified-id="Restarting-the-kernel-3.1">Restarting the kernel</a></span></li></ul></li><li><span><a href="#Very-basic-C++-syntax-(in-notebook-and-in-general)" data-toc-modified-id="Very-basic-C++-syntax-(in-notebook-and-in-general)-4">Very basic C++ syntax (in notebook and in general)</a></span><ul class="toc-item"><li><span><a href="#Semicolons" data-toc-modified-id="Semicolons-4.1">Semicolons</a></span></li><li><span><a href="#Blocks" data-toc-modified-id="Blocks-4.2">Blocks</a></span></li><li><span><a href="#Input-/-output" data-toc-modified-id="Input-/-output-4.3">Input / output</a></span></li></ul></li></ul></div>
%% Cell type:markdown id: tags:
## About the choice of a Jupyter notebook
This notebook uses up [xeus-cling](https://xeus-cling.readthedocs.io/en/latest/), a special instance of Jupyter able to run C++ code based upon xeus (tool to build Jupyter kernels for any language) and cling (a creation from CERN to be able to run C++ as an interpreted language).
The reasons for these choices is to really access directly to handle C++ code without the hassle of explaining how to compile and run stuff, which is an especially cumbersome way to start with this (or any really...) language.
This is not to say this tutorial will ignore entirely these topics (see the dedicated [chapter](/notebooks/6-InRealEnvironment/0-main.ipynb), just that we will first focus on C++ code. However keep in mind that this notebook's fancy interpreter is not a typical C++ environment.
Jupyter Xeus-Cling is still under active development: you should really get a recent version and keep it up-to-date. Some examples in this lecture didn't work at first and were properly dealt with with a version one month later!
## When the notebook is not enough...
As we shall see repeatedly, Xeus-cling notebooks are far from being full-proof: some stuff that are perfectly acceptable C++ aren't accepted in them, and some others required work-arounds. When such an issue appears:
* It will be indicated explicitly in the notebook if a specific work around is used. We do not want you to take Jupyter work-arounds as a legit advice on how to write proper C++.
* If Jupyter can't deal with the code, we will use [Coliru](https://coliru.stacked-crooked.com/). Coliru is a C++ online compiler; others are listed [here](https://arne-mertz.de/2017/05/online-compilers/) ([Wandbox](https://wandbox.org/) deserves a shout out as it enables testing the same code with a great variety of compiler versions).
%% Cell type:markdown id: tags:
## Few guidelines about Jupyter
You might not be familiar with Jupyter notebooks, so here are few tips to run it smoothly (the _Help_ menu will help you find more if you need it).
In a Jupyter notebook the content is divided into _cells_, in our case we are using two kind of cells:
* Markdown cells, such as the ones into these very words are written.
* Code cells, which are running code. In these notebooks the chosen kernel is C++17, so the code is C++17 which is interpreted by cling.
There are two modes:
* Edit mode, in which you might change the content of a cell. In this mode the left part of the cell is in green.
* Command mode, in which you might take actions such as changing the type of a cell, create or delete a new one, etc...
To enter in edit mode, simply type on 'Enter'.
To enter in command mode, type 'Esc'.
To execute a cell, type 'Shift + Enter'. For a markdown cell it will edit nicely the content by interpreting the markdown, and for a code cell it will run the code.
In command mode, several handy shortcuts are there; I would recommend especially:
* `a` (add a cell above)
* `b` (add a cell below)
* `x` (cut a cell)
* `M` (change cell mode to Markdown)
The complete list is available in _Help_ > _Keyboard_ shortcut.
If for some reason the code in the notebook seems stuck, you might try to restart the kernel with one of the restart option in the _Kernel_ menu.
### Restarting the kernel
Sometimes something that should work doesn't... In this case try restarting the kernel: it might fix your issue!
%% Cell type:markdown id: tags:
## Very basic C++ syntax (in notebook and in general)
### Semicolons
In C++ most instructions end by a semicolon `;`. If you forget it, the underlying compiler doesn't understand the syntax.
%% Cell type:code id: tags:
``` C++17
{
int foo = 5 // COMPILATION ERROR!
}
```
%%%% Output: stream
input_line_11:3:16: error: expected ';' at end of declaration
int foo = 5 // COMPILATION ERROR!
 ^
 ;

%%%% Output: error
Interpreter Error:
%% Cell type:code id: tags:
``` C++17
{
int foo = 5; // OK
}
```
%% Cell type:markdown id: tags:
Spaces, end lines and tabulations act as word separators; utterly unreadable code as the one below is perfectly fine from the compiler standpoint:
%% Cell type:code id: tags:
``` C++17
# include <string>
{
int nombre ; nombre = 1
; std::string nom;
nom=
"truc" ;
nombre = 2
;
}
```
%% Cell type:markdown id: tags:
### Blocks
You may have notices the braces `{` and `}` in the examples above. They are here a technicality to make cling interpreter work better in a Jupyter environment, but that is also useful in a real C++ code.
What is between both braces constitute a **block**; variables that are initialized inside are destroyed once the closing brace is past (don't worry - we will come back to that [later](/notebooks/1-ProceduralProgramming/6-DynamicAllocation.ipynb#Stack)).
What is between both braces constitute a **block**; variables that are initialized inside are destroyed once the closing brace is past (don't worry - we will come back to that [later](/notebooks/1-ProceduralProgramming/5-DynamicAllocation.ipynb#Stack)).
This is an incredibly useful feature: you may ensure this way that a variable is not kept any longer than necessary. We will come back to this feature (called the **locality of reference** later); let's see why they are useful in a notebook environment.
In C++, at a given scope a same variable can't be defined twice. So for instance if I defined twice a same variable, the compiler will yell about redefinition of a variable:
%% Cell type:code id: tags:
``` C++17
int i = 2; // Should be fine on the first call.
// But if you have executed it already, a new attempt will make the compiler yell.
```
%% Cell type:code id: tags:
``` C++17
int i = 1; // If the cell above was run, compilation error as `i` is already defined.
```
%%%% Output: stream
input_line_17:2:6: error: redefinition of 'i'
int i = 1; // If the cell above was run, compilation error as `i` is al...
 ^
input_line_16:2:6: note: previous definition is here
int i = 2; // Should be fine on the first call. But if you have execute...
 ^

%%%% Output: error
Interpreter Error:
%% Cell type:markdown id: tags:
The only way to circumvent this is to restart the kernel... and you may then run only one of this cell, and only once.
So you might now see the usefulness of the braces: by putting them I define the variables in a cell in a block, and it is only defined inside this block. Same variable may this way be defined in different cells:
%% Cell type:code id: tags:
``` C++17
{
int i = 2; // Fine
}
```
%% Cell type:code id: tags:
``` C++17
{
int i = 1; // Also fine
}
```
%% Cell type:markdown id: tags:
### Input / output
Inputs and outputs aren't directly a part of the language itself, but are in the standard library (often abbreviated as STL for Standard Template Library even if some purist may well and explain it's not 100 % the same thing...). You therefore need to __include__ a file named `iostream`; doing so will enable the use of the input / output facilities.
%% Cell type:code id: tags:
``` C++17
{
std::cout << "Hello world!" << std::endl; // Should fail (unless you run a cell that includes iostream before)
}
```
%%%% Output: stream
input_line_26:3:10: error: no member named 'cout' in namespace 'std'
std::cout << "Hello world!" << std::endl; // Should fail (unless yo...
 ~~~~~^

%%%% Output: error
Interpreter Error:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
std::cout << "Hello world!" << std::endl; // Should work: std::cout and std::endl are now known.
}
```
%%%% Output: stream
Hello world!
%% Cell type:markdown id: tags:
- `std::cout` is the symbol to designate the standard output
- `std::endl` is the symbol to clean-up the stream and go to next line.
The operator `<<` is used to indicate what you direct toward the stream; here std::cout << "Hello world!" tells to redirect the string toward the standard output.
We will see that a bit more in detail in [a later chapter](/notebooks/1-ProceduralProgramming/7-Streams.ipynb), but printing something is really helpful early on hence this brief introduction here.
We will see that a bit more in detail in [a later chapter](/notebooks/1-ProceduralProgramming/6-Streams.ipynb), but printing something is really helpful early on hence this brief introduction here.
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018_
_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)_
......
......@@ -15,9 +15,10 @@
"* [Condition and loops](/notebooks/1-ProceduralProgramming/2-Conditions-and-loops.ipynb)\n",
"* [Predefined types](/notebooks/1-ProceduralProgramming/3-Types.ipynb)\n",
"* [Functions](/notebooks/1-ProceduralProgramming/4-Functions.ipynb)\n",
"* [TP 1](/notebooks/1-ProceduralProgramming/5-TP-1.ipynb)\n",
"* [Dynamic allocation](/notebooks/1-ProceduralProgramming/6-DynamicAllocation.ipynb)\n",
"* [Input/output](/notebooks/1-ProceduralProgramming/7-Streams.ipynb)"
" * [TP 1](/notebooks/1-ProceduralProgramming/4b-TP.ipynb)\n",
"* [Dynamic allocation](/notebooks/1-ProceduralProgramming/5-DynamicAllocation.ipynb)\n",
"* [Input/output](/notebooks/1-ProceduralProgramming/6-Streams.ipynb)\n",
" * [TP 2](/notebooks/1-ProceduralProgramming/6b-TP.ipynb)"
]
},
{
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb)
%% Cell type:markdown id: tags:
* [Variables, initialisation, affectation](/notebooks/1-ProceduralProgramming/1-Variables.ipynb)
* [Condition and loops](/notebooks/1-ProceduralProgramming/2-Conditions-and-loops.ipynb)
* [Predefined types](/notebooks/1-ProceduralProgramming/3-Types.ipynb)
* [Functions](/notebooks/1-ProceduralProgramming/4-Functions.ipynb)
* [TP 1](/notebooks/1-ProceduralProgramming/5-TP-1.ipynb)
* [Dynamic allocation](/notebooks/1-ProceduralProgramming/6-DynamicAllocation.ipynb)
* [Input/output](/notebooks/1-ProceduralProgramming/7-Streams.ipynb)
* [TP 1](/notebooks/1-ProceduralProgramming/4b-TP.ipynb)
* [Dynamic allocation](/notebooks/1-ProceduralProgramming/5-DynamicAllocation.ipynb)
* [Input/output](/notebooks/1-ProceduralProgramming/6-Streams.ipynb)
* [TP 2](/notebooks/1-ProceduralProgramming/6b-TP.ipynb)
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018_
_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)_
......
......@@ -877,7 +877,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"**IMPORTANT**: so far we have considered only the case of _static_ arrays, for which the size is already known at compilation. We will deal with dynamic ones [later in this tutorial](/notebooks/1-ProceduralProgramming/6-DynamicAllocation.ipynb#Arrays-on-heap) (and also with the standard libraries [alternatives](/notebooks/5-UsefulConceptsAndSTL/3-Containers.ipynb) such as std::vector or std::array which are actually much more compelling)."
"**IMPORTANT**: so far we have considered only the case of _static_ arrays, for which the size is already known at compilation. We will deal with dynamic ones [later in this tutorial](/notebooks/1-ProceduralProgramming/5-DynamicAllocation.ipynb#Arrays-on-heap) (and also with the standard libraries [alternatives](/notebooks/5-UsefulConceptsAndSTL/3-Containers.ipynb) such as std::vector or std::array which are actually much more compelling)."
]
},
{
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [Predefined types](/notebooks/1-ProceduralProgramming/1-Variables.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="#Ordinary-variables" data-toc-modified-id="Ordinary-variables-1">Ordinary variables</a></span><ul class="toc-item"><li><span><a href="#Declaration" data-toc-modified-id="Declaration-1.1">Declaration</a></span></li><li><span><a href="#Initialisation" data-toc-modified-id="Initialisation-1.2">Initialisation</a></span></li><li><span><a href="#Affectation" data-toc-modified-id="Affectation-1.3">Affectation</a></span></li><li><span><a href="#Increment-and-decrement-operators" data-toc-modified-id="Increment-and-decrement-operators-1.4">Increment and decrement operators</a></span></li><li><span><a href="#Comparing-values" data-toc-modified-id="Comparing-values-1.5">Comparing values</a></span></li></ul></li><li><span><a href="#References" data-toc-modified-id="References-2">References</a></span></li><li><span><a href="#Pointers" data-toc-modified-id="Pointers-3">Pointers</a></span><ul class="toc-item"><li><span><a href="#nullptr" data-toc-modified-id="nullptr-3.1"><code>nullptr</code></a></span></li></ul></li><li><span><a href="#Constant-variables-and-pointers" data-toc-modified-id="Constant-variables-and-pointers-4">Constant variables and pointers</a></span></li><li><span><a href="#Arrays" data-toc-modified-id="Arrays-5">Arrays</a></span><ul class="toc-item"><li><span><a href="#Arrays-and-pointers" data-toc-modified-id="Arrays-and-pointers-5.1">Arrays and pointers</a></span></li></ul></li></ul></div>
%% Cell type:markdown id: tags:
## Ordinary variables
%% Cell type:markdown id: tags:
### Declaration
%% Cell type:markdown id: tags:
To be usable in a C++ program, a variable must be declared. This declaration shall include at least the type of
the variable, followed by its name and a semicolon.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int number; // integer variable
double real; // floating-point variable
std::cout << number << std::endl;
std::cout << real << std::endl;
}
```
%%%% Output: stream
32766
1.37345e-70
%% Cell type:markdown id: tags:
### Initialisation
%% Cell type:markdown id: tags:
Although not mandatory, it is strongly recommended to give a
initial value to your variables, as an expression between brackets.
Not providing an initial value may lead to unexpected behaviour. For instance you can't make hypothesis upon the values of `number` and `real` in the cell above: you might end-up with any value... and someone else might get other values on his computer!
If you give braces without values, a predefined and associated value
to the type is used (usually a form of 0).
If you give nothing , the behaviour is undefined: you might end up with a 0-like value or something
%% Cell type:code id: tags:
``` C++17
{
int nb1 { 1 }; // integer variable set with the value 1
int nb2 {}; // same as int nb2{0};
double pi { 3.14 } ; // real variable
}
```
%% Cell type:markdown id: tags:
C++ actually supports many other historical forms
of initialization, which you will encounter everywhere, including in this tutorial,
with brackets and/or an equal sign. There are some subtile differences
between each other... that you can ignore most of the time.
%% Cell type:code id: tags:
``` C++17
{
int a = 5;
int b(5);
int c = { 5 };
}
```
%% Cell type:markdown id: tags:
In all cases, even if there is an equal sign, it is important to remember
that it is an initialization, not an assignment (this will be
important when we will define our own types).
%% Cell type:markdown id: tags:
### Affectation
A new value is stored in an existing variable using the operator
of assignment `=`. The name of the variable is on the left; the expression
on the right of the `=` sign is evaluated, and its result is assigned to the variable.
%% Cell type:code id: tags:
``` C++17
#include <iostream> // for std::cout and std::endl
{
int a {}, b {}, c {} ; // default initialization; set the values to 0
std::cout << "Default initialization: a = " << a << ", b = " << b << " and c = " << c << std::endl;
a = 4;
b = 7;
c = a + b;
std::cout << "After affectations: a = " << a << ", b = " << b << " and c = " << c << std::endl;
}
```
%%%% Output: stream
Default initialization: a = 0, b = 0 and c = 0
After affectations: a = 4, b = 7 and c = 11
%% Cell type:markdown id: tags:
It is also possible to define (slightly) more advanced operators of assignments that modify the value currently stored by a simple operation:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a {}, b {}, c {} ; // default initialization; set the values to 0
a += 4; // add 4 to the current value of a
std::cout << "a = " << a << std::endl;
a *= 7; // multiply current value of a by 3
std::cout << "a = " << a << std::endl;
a /= 5; // divide a by 5 and assign the quotient to a
std::cout << "a = " << a << std::endl;
}
```
%%%% Output: stream
a = 4
a = 28
a = 5
%% Cell type:markdown id: tags:
### Increment and decrement operators
Finally, C++ also provides a shortcut when value is either incremented or decremented by adding `++` or `--` before or after the name of the variable.
* If the sign is placed before the variable, it is a **pre-increment**.
* If the sign is placed after the variable, it is a **post-increment**.
An example is the better way to explain the difference between both:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a = 5;
int b = 3;
a++; // increment a by 1.
++a; // same, both are actually equivalents here.
int c = a + b;
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
c = a-- + b; // first a + b is evaluated, and only then a is decremented.
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
c = a + ++b; // first b is incremented, and only then a + b is evaluated.
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
}
```
%%%% Output: stream
a = 7, b = 3 and c = 10
a = 6, b = 3 and c = 10
a = 6, b = 4 and c = 10
%% Cell type:markdown id: tags:
Honestly it's usually better to remove any ambiguity by separating explicitly both operations:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a = 7;
int b = 3;
int c = a + b;
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
--a; // equivalent to a-- but for reasons related to the standard library I advise you to rather use the
// pre-increment form.
c = a + b; // first a + b is evaluated, and only then a is decremented.
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
++b; // same: equivalent to b++;
c = a + b; // first a is incremented, and only then a + b is evaluated.
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
}
```
%%%% Output: stream
a = 7, b = 3 and c = 10
a = 6, b = 3 and c = 9
a = 6, b = 4 and c = 10
%% Cell type:markdown id: tags:
### Comparing values
As shown above, `=` is the affectation operator. To compare two values, the symbol to use is `==`.
Other arithmetic operators are:
| Operator | Effect |
|:------------- |:-------------------------------------------:|
| `a == b` | `true` if a and b are equals |
| `a != b` | `true` if a and b are different |
| `a < b` | `true` if a is less than b |
| `a > b` | `true` if a is greater than b |
| `a >= b` | `true` if a is greater than or equal to b |
| `a <= b` | `true` if a is less than or equal to b |
These operators are defined for most ordinary types and may be defined for your own types (we'll see that [later](/notebooks/3-Operators/2-Comparison.ipynb)).
%% Cell type:markdown id: tags:
## References
A reference is a variable that acts as a kind of alias, and provides another name for the same value.
When defining a reference, it must be immediately initialized by
indicating on which variable it should point; it cannot be changed after that.
The syntax is to add a `&` character just after the type:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a { 2 };
int b { a };
b = b + 1;
int& c { a }; // c is a reference to a
std::cout << "Initial values : a = " << a << ", b = " << b << " and c = " << c << std::endl;
b = b + 5;
std::cout << "After b is modified: a = " << a << ", b = " << b << " and c = " << c << std::endl;
c = c + 1 ; //
std::cout << "After c is modified: a = " << a << ", b = " << b << " and c = " << c << std::endl;
a *= 3;
std::cout << "After a is modified: a = " << a << ", b = " << b << " and c = " << c << std::endl;
}
```
%%%% Output: stream
Initial values : a = 2, b = 3 and c = 2
After b is modified: a = 2, b = 8 and c = 2
After c is modified: a = 3, b = 8 and c = 3
After a is modified: a = 9, b = 8 and c = 9
%% Cell type:markdown id: tags:
Reference is a purely C++ concept that doesn't exist in C.
%% Cell type:markdown id: tags:
## Pointers
A pointer contains the address of another variable. It declares itself by slipping
a `*` character before the name. It can be initialized or not with the address
of another variable. To explicitly extract the address of this other variable,
we use the symbol `&`.
Later, either you want to modify the address stored in the pointer, to which
in this case we use the pointer name directly, or we want to modify the variable
pointed, in which case the name is prefixed with `*`.
We can therefore see the pointer as a kind of redefinable reference.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a { 2 }, b { 10 };
int* p {&a}; // define a pointer p which is initialized with the address of a
std::cout << "Initial values: " << std::endl;
std::cout << "\t p: address = " << p << ", value = " << *p << std::endl;
std::cout << "\t a: address = " << &a << ", value = " << a << std::endl;
std::cout << "\t b: address = " << &b << ", value = " << b << std::endl;
std::cout << std::endl;
*p = *p + 1; // increment the integer value present at address p
std::cout << "After value pointed by p is incremented:" << std::endl;
std::cout << "\t p: address = " << p << ", value = " << *p << std::endl;
std::cout << "\t a: address = " << &a << ", value = " << a << std::endl;
std::cout << "\t b: address = " << &b << ", value = " << b << std::endl;
std::cout << std::endl;
p = &b; // change the address pointed by p
std::cout << "p now points to the address of b:" << std::endl;
std::cout << "\t p: address = " << p << ", value = " << *p << std::endl;
std::cout << "\t a: address = " << &a << ", value = " << a << std::endl;
std::cout << "\t b: address = " << &b << ", value = " << b << std::endl;
}
```
%%%% Output: stream
Initial values:
p: address = 0x7ffeea882974, value = 2
a: address = 0x7ffeea882974, value = 2
b: address = 0x7ffeea882970, value = 10
After value pointed by p is incremented:
p: address = 0x7ffeea882974, value = 3
a: address = 0x7ffeea882974, value = 3
b: address = 0x7ffeea882970, value = 10
p now points to the address of b:
p: address = 0x7ffeea882970, value = 10
a: address = 0x7ffeea882974, value = 3
b: address = 0x7ffeea882970, value = 10
%% Cell type:markdown id: tags:
### `nullptr`
A pointer can also designate no variables if initialized with the special value
`nullptr`. It is then a mistake to try to access its pointed value (as we shall see later, an [`assert`](/notebooks/5-UsefulConceptsAndSTL/1-ErrorHandling.ipynb#Assert) is a good idea to ensure we do not try to dereference a `nullptr` value).
It is strongly recommended to initialize a pointer when creating it: if you define an uninitialized pointer, it points to an arbitrary area of memory, which can create undefined behaviors that are not necessarily reproducible.
If the pointed area is known at initialization and never changes throughout the program, you should consider a reference rather than a pointer.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int* p = nullptr; // define a null pointer p
std::cout << "\t p: address = " << p << ", value = " << *p << std::endl; // Dereferencing p is misguided!
}
```
%%%% Output: stream
p: address = 0x0, value =
%%%% Output: stream
input_line_16:4:62: warning: null passed to a callee that requires a non-null argument [-Wnonnull]
std::cout << "\t p: address = " << p << ", value = " << *p << std::endl; // Dereferencing p is misguided!
 ^

%%%% Output: error
Interpreter Exception:
%% Cell type:markdown id: tags:
## Constant variables and pointers
When declaring a variable of a predefined type, it is possible to specify its value can't be changed afterward by using the word `const` which may be placed before or after the type:
%% Cell type:code id: tags:
``` C++17
{
const double pi = 3.1415927;
double const pi_2 = 3.1415927; // equally valid; it is just a matter of taste. Mine is to put it before,
// so that is what you will see in the remaining of the tutorial.
pi = 5.; // COMPILATION ERROR!
pi_2 = 7.; // COMPILATION ERROR!
}
```
%%%% Output: stream
input_line_23:6:8: error: cannot assign to variable 'pi' with const-qualified type 'const double'
pi = 5.; // COMPILATION ERROR!
 ~~ ^
input_line_23:3:18: note: variable 'pi' declared const here
const double pi = 3.1415927;
 ~~~~~~~~~~~~~^~~~~~~~~~~~~~
input_line_23:7:10: error: cannot assign to variable 'pi_2' with const-qualified type 'const double'
pi_2 = 7.; // COMPILATION ERROR!
 ~~~~ ^
input_line_23:4:18: note: variable 'pi_2' declared const here
double const pi_2 = 3.1415927; // equally valid; it is just a matter of taste. Mine is to put it before,
 ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~

%%%% Output: error
Interpreter Error:
%% Cell type:markdown id: tags:
In the case of a pointer, we can declare the pointer itself constant, and/or the value pointed to depending on whether we place the keyword `const` before or after the type name (in this case it applies to the pointed value) or after the character `*` (in this case it applies to the pointer itself):
%% Cell type:code id: tags:
``` C++17
{
int a { 2 }, b { 3 };
int* p { &a }; // Both pointer and pointed values are modifiable.
p = &b; // OK
*p = 5; // OK
}
```
%% Cell type:code id: tags:
``` C++17
{
int a { 2 }, b { 3 };
int* const p { &a }; // Value is modifiable, but not the address pointed to.
*p = 5; // OK
p = &b; // COMPILATION ERROR - if you comment it it will
}
```
%%%% Output: stream
input_line_74:6:7: error: cannot assign to variable 'p' with const-qualified type 'int *const'
p = &b; // COMPILATION ERROR - if you comment it it will
 ~ ^
input_line_74:4:16: note: variable 'p' declared const here
int* const p { &a }; // Value is modifiable, but not the address pointed to.
 ~~~~~~~~~~~^~~~~~~~

%%%% Output: error
Interpreter Error:
%% Cell type:code id: tags:
``` C++17
{
int a { 2 }, b { 3 };
const int* p { &a }; // Address pointed to is modifiable, but not the underlying value.
p = &b; // OK
*p = 5; // COMPILATION ERROR
}
```
%%%% Output: stream
input_line_75:7:8: error: read-only variable is not assignable
*p = 5; // COMPILATION ERROR
 ~~ ^

%%%% Output: error
Interpreter Error:
%% Cell type:code id: tags:
``` C++17
{
int a { 2 }, b { 3 };
const int* const p { &a }; // Nothing is modifiable
p = &b; // COMPILATION ERROR
*p = 5; // COMPILATION ERROR
}
```
%%%% Output: stream
input_line_76:6:7: error: cannot assign to variable 'p' with const-qualified type 'const int *const'
p = &b; // COMPILATION ERROR
 ~ ^
input_line_76:4:22: note: variable 'p' declared const here
const int* const p { &a }; // Nothing is modifiable
 ~~~~~~~~~~~~~~~~~^~~~~~~~
input_line_76:7:8: error: read-only variable is not assignable
*p = 5; // COMPILATION ERROR
 ~~ ^

%%%% Output: error
Interpreter Error:
%% Cell type:markdown id: tags:
**IMPORTANT**: Even if declared `const`, the pointed value is
not intrinsically constant. It just can't be
modified through this precise pointer.
If other variables reference the same memory area and
are not constant, they are able to modify the value:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a { 2 }, b { 3 };
const int* p { &a }; // Address pointed to is modifiable, but not the underlying value.
std::cout << "Value pointed by p (which doesn't allow value modification) is: " << *p << std::endl;
int* p2 {&a}; // Pointer to the same memory area, but no constness here.
*p2 = 10;
std::cout << "Value pointed by p (and modified through p2) is: " << *p << std::endl;
}
```
%%%% Output: stream
Value pointed by p (which doesn't allow value modification) is: 2
Value pointed by p (and modified through p2) is: 10
%% Cell type:markdown id: tags:
On the other hand, pointers can't be used as a work-around to modify a constant value:
%% Cell type:code id: tags:
``` C++17
{
const int n { 3 };
int* p_n { &n }; // COMPILATION ERROR
const int* p_n2 { &n }; // OK
}
```
%%%% Output: stream
input_line_25:4:16: error: cannot initialize a variable of type 'int *' with an rvalue of type 'const int *'
int* p_n { &n }; // COMPILATION ERROR
 ^~

%%%% Output: error
Interpreter Error:
%% Cell type:markdown id: tags:
## Arrays
The operator `[]` enables the creation of an array.
**Beware:** In C++, the indexes of an array start at 0.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int i[10] ; // Array of 10 integers - not initialised properly!
double x[3] = { 1., 2., 3. } ; // Array of 3 reals.
std::cout << "i[2] = " << i[2] << " (may be gibberish: undefined behaviour due to lack of initialization!)" << std::endl;
std::cout << "i[10] = " << i[10] << " (undefined behaviour: out of range. Warning identifies the issue)" << std::endl ;
std::cout << "x[1] = " << x[1] << " (expected: 2.)" << std::endl ;
}
```
%%%% Output: stream
input_line_18:7:32: warning: array index 10 is past the end of the array (which contains 10 elements) [-Warray-bounds]
std::cout << "i[10] = " << i[10] << " (undefined behaviour: out of range. Warning identifies the issue)" << std::endl ;
 ^ ~~
input_line_18:3:5: note: array 'i' declared here
int i[10] ; // Array of 10 integers - not initialised properly!
 ^

%%%% Output: stream
i[2] = 149375929 (may be nonsense: undefined behaviour due to lack of initialization!)
i[10] = -87424866 (undefined behaviour: out of range. Warning identifies the issue)
x[1] = 2 (expected: 2.)
%% Cell type:markdown id: tags:
Multi-dimensional arrays are also possible:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int k[2][3] { { 5, 7, 0 }, { 3, 8, 9 }};
std::cout << "k[0][0] = " << k[0][0] << " (expected: 5)" << std::endl;
std::cout << "k[1][2] = " << k[1][2] << " (expected: 9)" << std::endl;
int l[2][3] {}; // default initialization of all elements of the array.
std::cout << "l[0][0] = " << l[0][0] << " (expected: 0)" << std::endl;
std::cout << "l[1][2] = " << l[1][2] << " (expected: 0)" << std::endl;
}
```
%%%% Output: stream
k[0][0] = 5 (expected: 5)
k[1][2] = 9 (expected: 9)
l[0][0] = 0 (expected: 0)
l[1][2] = 0 (expected: 0)
%% Cell type:markdown id: tags:
**IMPORTANT**: so far we have considered only the case of _static_ arrays, for which the size is already known at compilation. We will deal with dynamic ones [later in this tutorial](/notebooks/1-ProceduralProgramming/6-DynamicAllocation.ipynb#Arrays-on-heap) (and also with the standard libraries [alternatives](/notebooks/5-UsefulConceptsAndSTL/3-Containers.ipynb) such as std::vector or std::array which are actually much more compelling).
**IMPORTANT**: so far we have considered only the case of _static_ arrays, for which the size is already known at compilation. We will deal with dynamic ones [later in this tutorial](/notebooks/1-ProceduralProgramming/5-DynamicAllocation.ipynb#Arrays-on-heap) (and also with the standard libraries [alternatives](/notebooks/5-UsefulConceptsAndSTL/3-Containers.ipynb) such as std::vector or std::array which are actually much more compelling).
%% Cell type:markdown id: tags:
### Arrays and pointers
The variable designating an array is similar to a constant pointer pointing to the beginning of the array. Increasing this pointer is like moving around in the array.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int i[2] = { 10, 20 } ;
std::cout << *i << " (expected: 10)" << std::endl ;
std::cout << *i + 1 << " (expected: 11)" << std::endl ;
std::cout << *(i + 1) << " (expected: 20)" << std::endl ;
int* j = i; // OK!
++j; // OK!
std::cout << "Value pointed by j is " << *j << " (expected: 20)" << std::endl;
}
```
%%%% Output: stream
10 (expected: 10)
11 (expected: 11)
20 (expected: 20)
Value pointed by j is 20 (expected: 20)
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018_
_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)_
......
......@@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [TP 1](/notebooks/1-ProceduralProgramming/5-TP1.ipynb)"
"# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [TP 1](/notebooks/1-ProceduralProgramming/4b-TP.ipynb)"
]
},
{
......@@ -23,7 +23,7 @@
"source": [
"## How to do this TP\n",
"\n",
"There are basically three ways to do the exercices proposed in this TP:\n",
"There are basically three ways to do the exercices proposed in this TP; using Jupyter is not one of them!\n",
"\n",
"### Through a dedicated Web site\n",
"\n",
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [TP 1](/notebooks/1-ProceduralProgramming/5-TP1.ipynb)
# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [TP 1](/notebooks/1-ProceduralProgramming/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="#How-to-do-this-TP" data-toc-modified-id="How-to-do-this-TP-1">How to do this TP</a></span><ul class="toc-item"><li><span><a href="#Through-a-dedicated-Web-site" data-toc-modified-id="Through-a-dedicated-Web-site-1.1">Through a dedicated Web site</a></span></li><li><span><a href="#Through-Jupyter" data-toc-modified-id="Through-Jupyter-1.2">Through Jupyter</a></span></li><li><span><a href="#On-your-local-machine" data-toc-modified-id="On-your-local-machine-1.3">On your local machine</a></span></li></ul></li><li><span><a href="#The-problem-we'll-deal-with-throughout-the-TPs" data-toc-modified-id="The-problem-we'll-deal-with-throughout-the-TPs-2">The problem we'll deal with throughout the TPs</a></span><ul class="toc-item"><li><span><a href="#EXERCICE-1:-Adding-a-loop" data-toc-modified-id="EXERCICE-1:-Adding-a-loop-2.1"><strong>EXERCICE 1: Adding a loop</strong></a></span></li><li><span><a href="#EXERCICE-2:-Adding-a-function" data-toc-modified-id="EXERCICE-2:-Adding-a-function-2.2"><strong>EXERCICE 2: Adding a function</strong></a></span></li><li><span><a href="#EXERCICE-3:-compute-the-approximation" data-toc-modified-id="EXERCICE-3:-compute-the-approximation-2.3"><strong>EXERCICE 3: compute the approximation</strong></a></span></li><li><span><a href="#EXERCICE-4:-Search-for-the-best-approximation-for-a-given-maximum-numerator." data-toc-modified-id="EXERCICE-4:-Search-for-the-best-approximation-for-a-given-maximum-numerator.-2.4"><strong>EXERCICE 4: Search for the best approximation for a given maximum numerator.</strong></a></span></li><li><span><a href="#EXERCICE-5:-Computation-of-the-maximum-numerator-as-a-function-of-the-number-of-bits" data-toc-modified-id="EXERCICE-5:-Computation-of-the-maximum-numerator-as-a-function-of-the-number-of-bits-2.5"><strong>EXERCICE 5: Computation of the maximum numerator as a function of the number of bits</strong></a></span></li><li><span><a href="#EXERCICE-6:-moving-display-to-a-new-intermediate-function" data-toc-modified-id="EXERCICE-6:-moving-display-to-a-new-intermediate-function-2.6"><strong>EXERCICE 6: moving display to a new intermediate function</strong></a></span></li><li><span><a href="#EXERCICE-7:-adding-error-in-display" data-toc-modified-id="EXERCICE-7:-adding-error-in-display-2.7"><strong>EXERCICE 7: adding error in display</strong></a></span></li><li><span><a href="#EXERCICE-8:-Multiplication" data-toc-modified-id="EXERCICE-8:-Multiplication-2.8"><strong>EXERCICE 8: Multiplication</strong></a></span></li><li><span><a href="#EXERCICE-9:-display-for-multiply" data-toc-modified-id="EXERCICE-9:-display-for-multiply-2.9"><strong>EXERCICE 9: display for multiply</strong></a></span></li><li><span><a href="#EXERCICE-10:-print-error-in-display_multiply()" data-toc-modified-id="EXERCICE-10:-print-error-in-display_multiply()-2.10"><strong>EXERCICE 10: print error in <code>display_multiply()</code></strong></a></span></li></ul></li></ul></div>
%% Cell type:markdown id: tags:
## How to do this TP
There are basically three ways to do the exercices proposed in this TP:
There are basically three ways to do the exercices proposed in this TP; using Jupyter is not one of them!
### Through a dedicated Web site
<a href="https://coliru.stacked-crooked.com/">Coliru</a> or <a href="https://wandbox.org/">Wandbox</a> enables to write and execute C++ programs in a Web browser.
They are the easiest way to go: nothing to configure on your machine and none of the pesky limitations of a Jupyter xeus-cling notebook.
Just remember you need to define in your cpp file a __main__ function!
### Through Jupyter
What you have done so far in following the theoretical parts of this tutorial. It is however not very practical for a TP: if you write a function and then want to modify it, you need to either restart the kernel or rename the function.
This provides a not very practial workflow, and I would advise against it.
### On your local machine
This implies a compiler is already installed on your machine and ready to use.
To compile your program:
* Either you're inside an IDE you're already proficient with, and this IDE is able to compile your code.
* Or you have to use the command line, for instance:
`g++ -std=c++17 -o my_executable program.cpp`
where you of course replace `my_executable` and `program.cpp` by whatever you wish. `g++` might be replaced by `clang++` or whatever compiler you're using.
Once compiled, you may execute the program:
`./my_executable`
The `./` above is there in case current folder is not in your `$PATH`.
We'll deal more throughly with C++ in a _real_ environment at the end of this tutorial.
## The problem we'll deal with throughout the TPs
Any real number can be approximated by the ratio between two integers.
We will choose here to approximate any real `r` by an expression `numerator / 2^exponent`, where both `numerator` and `exponent` are integers.
The higher the numerator and exponent values, the more accurate the approximation can be. For example, 0.65 can be approximated successively by:
* 1 / 2<sup>1</sup> = 0.5
* 3 / 2<sup>2</sup> = 0.75
* 5 / 2<sup>3</sup> = 0.625
* ...
The highest possible numbers will therefore be chosen, within the limits set by the system, i.e. by the number of bits available to encode these numbers.
As part of the TP, the number of bits allowed to store the numerator will be arbitrarily fixed, and the effect on the accuracy of the approximation will be calculated. Note that if you have N bits to store an integer, the largest possible integer is 2<sup>N</sup> - 1.
The TP will be subdivided in multiple exercices; copy/paste in your editor of choice the snippets below.
%% Cell type:markdown id: tags:
### __EXERCICE 1: Adding a loop__
Write a loop that displays the like of above up to the 2<sup>8</sup> for 0.65.
_Expected result_:
0.65 ~ 1 / 2^1
0.65 ~ 3 / 2^2
0.65 ~ 5 / 2^3
0.65 ~ 10 / 2^4
0.65 ~ 21 / 2^5
0.65 ~ 42 / 2^6
0.65 ~ 83 / 2^7
0.65 ~ 166 / 2^8
%% Cell type:markdown id: tags:
### __EXERCICE 2: Adding a function__
Introduce in the previous program a function called `display_power_of_2_approx(...), which takes as argument the actual value to be approximated. Modify the main program to call this function with values 0.65 and 0.35. The main program must be:
%% Cell type:code id: tags:
``` C++17
int main()
{
display_power_of_2_approx(.65);
display_power_of_2_approx(.35);
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
_Expected result_:
0.65 ~ 1 / 2^1
0.65 ~ 3 / 2^2
0.65 ~ 5 / 2^3
0.65 ~ 10 / 2^4
0.65 ~ 21 / 2^5
0.65 ~ 42 / 2^6
0.65 ~ 83 / 2^7
0.65 ~ 166 / 2^8.
0.35 ~ 1 / 2^1
0.35 ~ 1 / 2^2
0.35 ~ 3 / 2^3
0.35 ~ 6 / 2^4
0.35 ~ 11 / 2^5
0.35 ~ 22 / 2^6
0.35 ~ 45 / 2^7
0.35 ~ 90 / 2^8
%% Cell type:markdown id: tags:
### __EXERCICE 3: compute the approximation__
Add in the `display_power_of_2_approx()` function the display of the approximate value, which is calculated by dividing each numerator by the power of two associated. Be careful, dividing an integer by an integer returns an integer: for instance 3 / 4 is 0. A division returns a real if one of the terms is real: 3. / 4 or `static_cast<double>`(3) / 4 is 0.75.
The expected display is as follows:
0.65 ~ 1 / 2^1 (0.5)
0.65 ~ 3 / 2^2 (0.75)
0.65 ~ 5 / 2^3 (0.625)
0.65 ~ 10 / 2^4 (0.625)
0.65 ~ 21 / 2^5 (0.65625)
0.65 ~ 42 / 2^6 (0.65625)
0.65 ~ 83 / 2^7 (0.648438)
0.65 ~ 166 / 2^8 (0.648438)
0.35 ~ 1 / 2^1 (0.5)
0.35 ~ 1 / 2^2 (0.25)
0.35 ~ 3 / 2^3 (0.375)
0.35 ~ 6 / 2^4 (0.375)
0.35 ~ 11 / 2^5 (0.34375)
0.35 ~ 22 / 2^6 (0.34375)
0.35 ~ 45 / 2^7 (0.351562)
0.35 ~ 90 / 2^8 (0.351562)
%% Cell type:markdown id: tags:
### __EXERCICE 4: Search for the best approximation for a given maximum numerator.__
The larger the numerator and the exponent to the denominator, the more accurate the approximation.
In `display_power_of_2_approx()`, modify the loop so that it looks for the best numerator / exponent pair, without the numerator exceeding a certain maximum value, passed as an argument to the function.
Keep the display only for this best solution, and add in this display the value of the maximum allowed numerator.
Use the following main function to check it works:
%% Cell type:code id: tags:
``` C++17
int main()
{
display_power_of_2_approx(15, 0.65) ;
display_power_of_2_approx(255, 0.65) ;
display_power_of_2_approx(15, 0.35) ;
display_power_of_2_approx(255, 0.35) ;
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
_Expected result_ is something like:
````
0.65 ~ 10 / 2^4 = 0.625 [with numerator < 15]
0.65 ~ 166 / 2^8 = 0.648438 [with numerator < 255]
0.35 ~ 11 / 2^5 = 0.34375 [with numerator < 15]
0.35 ~ 179 / 2^9 = 0.349609 [with numerator < 255]
````
%% Cell type:markdown id: tags:
### __EXERCICE 5: Computation of the maximum numerator as a function of the number of bits__
The highest usable value for the numerator depends on the number of bits used to represent this integer.
In `display_power_of_2_approx()` arguments, replace the argument designating the maximum value with an argument designating the maximum number of bits and correct the body of the function accordingly, using the `max_int()` function given below.
On display, replace the maximum numerator with the number of bits.
In the main program, make two loops that vary the number of bits from 2 to 8 in steps of 2, one that calls approxime(...) for 0.65, and the other for 0.35.
%% Cell type:code id: tags:
``` C++17
// Maximum integer that might be represented with `Nbits` bits.
int max_int(int Nbits)
{
return (times_power_of_2(1, Nbits) - 1);
}
```
%% Cell type:markdown id: tags:
_Expected result_:
````
0.65 ~ 1 / 2^1 (0.5) [for 2 bits]
0.65 ~ 10 / 2^4 (0.625) [for 4 bits]
0.65 ~ 42 / 2^6 (0.65625) [for 6 bits]
0.65 ~ 166 / 2^8 (0.648438) [for 8 bits]
0.35 ~ 1 / 2^2 (0.25) [for 2 bits]
0.35 ~ 11 / 2^5 (0.34375) [for 4 bits]
0.35 ~ 45 / 2^7 (0.351562) [for 6 bits]
0.35 ~ 179 / 2^9 (0.349609) [for 8 bits]
````
%% Cell type:markdown id: tags:
### __EXERCICE 6: moving display to a new intermediate function__
Currently, `display_power_of_2_approx()` is in charge of two different operations:
* Computing the values.
* Displaying them on screen.
It is often advisable to give one main functionality to a given function; we will thefore separate here the computation from the display.
Write a new function named `compute_power_of_2_approx()`, which computes the approximation itself and returns the numerator and the exponent as output arguments; this function will be called in `display_power_of_2_approx()`.
Output should remain the same!
%% Cell type:markdown id: tags:
### __EXERCICE 7: adding error in display__
Add in `display_power_of_2_approx()` the relative error of the approximation, computed by
%% Cell type:markdown id: tags:
````
(number - approx) / number
````
presented as a percentage and rounded to the nearest integer.
_Expected result_:
````
0.65 ~ 3 / 2^2 (0.75) [error = 15 % for 2 bits]
0.65 ~ 10 / 2^4 (0.625) [error = 4 % for 4 bits]
0.65 ~ 42 / 2^6 (0.65625) [error = 1 % for 6 bits]
0.65 ~ 166 / 2^8 (0.648438) [error = 0 % for 8 bits]
0.35 ~ 3 / 2^3 (0.375) [error = 7 % for 2 bits]
0.35 ~ 11 / 2^5 (0.34375) [error = 2 % for 4 bits]
0.35 ~ 45 / 2^7 (0.351562) [error = 0 % for 6 bits]
0.35 ~ 179 / 2^9 (0.349609) [error = 0 % for 8 bits]
````
%% Cell type:markdown id: tags:
### __EXERCICE 8: Multiplication__
Write the `multiply()` function that calculates the approximate product of a real by an integer coefficient and returns an integer.
This function must approximate the real using the `compute_power_of_2_approx()` function above; the returned integer shall use `times_power_of_2()` function.
The arguments of `multiply()` are the maximum number of bits for approximation, the real and the integer coefficient.
The new main will be:
%% 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
for (int Nbits = 2; Nbits <= 8; Nbits += 2)
display_power_of_2_approx(0.65, Nbits);
std::cout << std::endl;
for (int Nbits = 2; Nbits <= 8; Nbits += 2)
display_power_of_2_approx(0.35, Nbits);
std::cout << std::endl;
for (int bits = 1; bits <= 8; ++bits)
{
double exact = 0.65 * 3515 + 0.35 * 4832;
int rounded = round_as_int(exact);
int approx = multiply(bits, 0.65, 3515) + multiply(bits, 0.35, 4832);
std::cout << bits << " bits : 0.65 * 3515 + 0.35 * 4832 = " << rounded << " ~ " << approx << std::endl;
}
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
_Expected result:_
````
0.65 ~ 3 / 2^2 (0.75) [error = 15 % for 2 bits]
0.65 ~ 10 / 2^4 (0.625) [error = 4 % for 4 bits]
0.65 ~ 42 / 2^6 (0.65625) [error = 1 % for 6 bits]
0.65 ~ 166 / 2^8 (0.648438) [error = 0 % for 8 bits]
0.35 ~ 3 / 2^3 (0.375) [error = 7 % for 2 bits]
0.35 ~ 11 / 2^5 (0.34375) [error = 2 % for 4 bits]
0.35 ~ 45 / 2^7 (0.351562) [error = 0 % for 6 bits]
0.35 ~ 179 / 2^9 (0.349609) [error = 0 % for 8 bits]
1 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 2965
2 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4448
3 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4008
4 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3857
5 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3967
6 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4004
7 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3977
8 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3968
````
%% Cell type:markdown id: tags:
### __EXERCICE 9: display for multiply__
Exactly as we did previously in exercice 2, move the display of multiplication result in a dedicated function `display_multiply()` (which will hence takes 5 arguments: number of bits, two reals and their associated integer coefficient).
New main will 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
for (int Nbits = 2; Nbits <= 8; Nbits += 2)
display_power_of_2_approx(0.65, Nbits);
std::cout << std::endl;
for (int Nbits = 2; Nbits <= 8; Nbits += 2)
display_power_of_2_approx(0.35, Nbits);
std::cout << std::endl;
for (int Nbits = 1; Nbits <= 8; ++Nbits)
display_multiply(Nbits, 0.65, 3515, 0.35, 4832);
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
### __EXERCICE 10: print error in `display_multiply()`__
Modify slightly the function defined above to add display of the error; we will express it over 1000.
_Expected result_:
````
0.65 ~ 3 / 2^2 (0.75) [error = 15 % for 2 bits]
0.65 ~ 10 / 2^4 (0.625) [error = 4 % for 4 bits]
0.65 ~ 42 / 2^6 (0.65625) [error = 1 % for 6 bits]
0.65 ~ 166 / 2^8 (0.648438) [error = 0 % for 8 bits]
0.35 ~ 3 / 2^3 (0.375) [error = 7 % for 2 bits]
0.35 ~ 11 / 2^5 (0.34375) [error = 2 % for 4 bits]
0.35 ~ 45 / 2^7 (0.351562) [error = 0 % for 6 bits]
0.35 ~ 179 / 2^9 (0.349609) [error = 0 % for 8 bits]
1 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 2965 [error = 254 / 1000]
2 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4448 [error = 119 / 1000]
3 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4008 [error = 8 / 1000]
4 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3857 [error = 30 / 1000]
5 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3967 [error = 2 / 1000]
6 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4004 [error = 7 / 1000]
7 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3977 [error = 0 / 1000]
8 bits : 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3968 [error = 2 / 1000]
````
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018_
_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)_
......
......@@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [Dynamic allocations](/notebooks/1-ProceduralProgramming/6-DynamicAllocation.ipynb)"
"# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [Dynamic allocations](/notebooks/1-ProceduralProgramming/5-DynamicAllocation.ipynb)"
]
},
{
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [Dynamic allocations](/notebooks/1-ProceduralProgramming/6-DynamicAllocation.ipynb)
# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [Dynamic allocations](/notebooks/1-ProceduralProgramming/5-DynamicAllocation.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="#Stack" data-toc-modified-id="Stack-2">Stack</a></span></li><li><span><a href="#Heap-and-free-store" data-toc-modified-id="Heap-and-free-store-3">Heap and free store</a></span><ul class="toc-item"><li><span><a href="#Free-store?" data-toc-modified-id="Free-store?-3.1">Free store?</a></span></li></ul></li><li><span><a href="#Arrays-on-heap" data-toc-modified-id="Arrays-on-heap-4">Arrays on heap</a></span></li></ul></div>
%% Cell type:markdown id: tags:
## Introduction
In C++, we can finely control the life cycle of objects and manage the memory allocated to them. This is what makes it possible to create more powerful applications than with other languages, but it is also the main source of errors in the language. Pointers and dynamic memory management: watch out for danger!
## Stack
The ordinary variables of C++ have a lifetime limited to the current instruction block, whether it is the current function, or a instruction block attached to an `if`, `for` or just independant.
The memory allocated to them is located in an area called a **stack**, and is automatically relieved when exiting the current block using the **last in, first out** principle.
%% Cell type:code id: tags:
``` C++17
{
{
int a { 5 };
double b { 7.4 };
} // at the end of this block, b is released first and then a - but 99.99 % of the time you shouldn't care
// about that order!
// a and b are not available here
}
```
%% Cell type:markdown id: tags:
There are few limitations with the stack:
* The number of memory you can allocate on the stack is rather limited. On a current POSIX OS the order of magnitude is ~ 8 Mo (on Unix type `ulimit -a` in a terminal to get this information). If you allocate more you will get a **stack overflow** (and now you know why the [most popular developers forum](https://stackoverflow.com/) is named this way!)
* The information is very local; you can't use it elsewhere. If you pass the variable as argument in a function for instance a copy is made (or if you're using a reference or a pointer you enbe sure all is done when the block is exited!)
* Stack information must be known at compile time: if you're allocating an array on the stack you must know its size beforehand!
%% Cell type:markdown id: tags:
## Heap and free store
You can in fact also explicitly place a variable in another memory area called **heap** or **free store**; doing so overcomes the stack limitations mentioned above.
This is done by calling the `new` operator, which reserves the memory and returns its address, so that the user can store it _in a pointer_.
The **heap** is independent of the **stack** and the variable thus created exists as long as the `delete` operator is not explicitly called. The creation and destruction of this type of variable is the responsibility of the programmer.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int* n = new int(5); // variable created on the heap and initialized with value 5.
std::cout << *n << std::endl;
delete n; // deletion must be explicitly called; if not there is a memory leak!
}
```
%%%% Output: stream
5
%% Cell type:markdown id: tags:
What is especially tricky is that:
* Creating and destroying can be done in places very disconnected in your program.
* You must ensure that whatever the runtime path used in your program each variable allocated on the heap:
- is destroyed (otherwise you get a **memory leak**)
- is only destroyed once (or your program will likely crash with a message about **double deletion**).
In sophisticated programs, this could lead in serious and tedious bookkeeping to ensure all variables are properly handled, even if tools such as [Valgrind](http://www.valgrind.org/) or [Address sanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) may help to find out those you will probably have forgotten somewhere along the way.
To be honest, C++ gets quite a bad name due to this tedious memory handling; fortunately the RAII idiom provides a neat way to automatize nicely memory management (which we'll study [later](/notebooks/5-UsefulConceptsAndSTL/2-RAII.ipynb)) and some vocal critics on forums that regret the lack of [garbage collection](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) might actually not be aware of this fundamental (from my point of view at least) idiom.
%% Cell type:markdown id: tags:
### Free store?
**Free store** is very similar in functionality to the **heap** (to the point I had to [check the difference](https://stackoverflow.com/questions/1350819/c-free-store-vs-heap) before writing this...) , and more often than not one word might be used as the other. If you want to be pedantic:
* When memory is handled by `new`/`delete`, you should talk about **free store**.
* When memory is handled by `malloc`/`free` (the C functions), you should talk about **heap**.
Pedantry aside, the important thing to know is to never mix both syntax: if you allocate memory by `new` don't use `free` to relieve it.
%% Cell type:markdown id: tags:
## Arrays on heap
If you want to init an array which size you do not know at compile time or that might overflow the stack, you may to do with `new` syntax mixed with `[]`:
%% Cell type:code id: tags:
``` C++17
{
int* pi_first_five_digits = new int[5];
pi_first_five_digits[0] = 3;
pi_first_five_digits[1] = 1;
pi_first_five_digits[2] = 4;
pi_first_five_digits[3] = 1;
pi_first_five_digits[4] = 5;
delete[] pi_first_five_digits;
}
```
%% Cell type:markdown id: tags:
Please notice that:
* No value can be assigned in construction: you must first allocate the memory for the array and only in a second time fill it.
* A `[]` **must** be added to the **delete** instruction to indicate to the compiler this is actually an array that is destroyed.
In fact, my advice would be to avoid entirely to deal directly with such arrays and use containers from the standard library such as `std::vector`:
%% Cell type:code id: tags:
``` C++17
#include <vector>
{
std::vector<int> pi_first_five_digits { 3, 1, 4, 1, 5 };
}
```
%% Cell type:markdown id: tags:
that does the exact same job in a shorter way and is much more secure to use.
We shall see `std::vector` more deeply [later](/notebooks/5-UsefulConceptsAndSTL/3-Containers.ipynb) but will nonetheless use it before this as it is a rather elementary brick in most C++ codes.
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018_
_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)_
......
......@@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [Input and output streams](/notebooks/1-ProceduralProgramming/7-Streams.ipynb)"
"# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [Input and output streams](/notebooks/1-ProceduralProgramming/6-Streams.ipynb)"
]
},
{
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [Input and output streams](/notebooks/1-ProceduralProgramming/7-Streams.ipynb)
# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [Input and output streams](/notebooks/1-ProceduralProgramming/6-Streams.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="#Predefined-streams" data-toc-modified-id="Predefined-streams-1">Predefined streams</a></span><ul class="toc-item"><li><span><a href="#std::cout" data-toc-modified-id="std::cout-1.1"><code>std::cout</code></a></span></li><li><span><a href="#std::cerr" data-toc-modified-id="std::cerr-1.2"><code>std::cerr</code></a></span></li><li><span><a href="#std:cin" data-toc-modified-id="std:cin-1.3"><code>std:cin</code></a></span></li></ul></li><li><span><a href="#Input/output-with-files" data-toc-modified-id="Input/output-with-files-2">Input/output with files</a></span><ul class="toc-item"><li><span><a href="#getline()" data-toc-modified-id="getline()-2.1"><code>getline()</code></a></span></li></ul></li><li><span><a href="#ostream-and-istream" data-toc-modified-id="ostream-and-istream-3"><code>ostream</code> and <code>istream</code></a></span></li><li><span><a href="#Conversion" data-toc-modified-id="Conversion-4">Conversion</a></span></li><li><span><a href="#Formatting-and-manipulators" data-toc-modified-id="Formatting-and-manipulators-5">Formatting and manipulators</a></span></li></ul></div>
%% Cell type:markdown id: tags:
## Predefined streams
The standard C++ includes an input/output library that specifies a common interface for all data exchanges with the outside world, based in particular on the insertion `<<` and extraction `>>` operators.
### `std::cout`
We have already dealt profusely with `std::cout` which provide the link to the Unix channel `stdout`:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
std::cout << "Hello world!" << std::endl;
}
```
%% Cell type:markdown id: tags:
### `std::cerr`
There is also `std::cerr`, which is related to Unix `stderr`:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int n = -4;
if (n < 0)
std::cerr << "Positive or null value expected!" << std::endl;
}
```
%% Cell type:markdown id: tags:
### `std:cin`
And finally `std::cin`, related to Unix channel `stdin`. Line crossings are ignored (assimilated to spaces and tabs).
**WARNING** This works only with a recent version of Xeus-cling.
%% Cell type:code id: tags:
``` C++17
#include <random>
#include <iostream>
{
std::random_device rd; // Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()
std::uniform_int_distribution<> dis(0, 100);
auto hidden = dis(gen);
int guess = -1;
while (guess != hidden)
{
std::cout << "Find the value between 0 and 100: ";
std::cin >> guess;
if (guess > hidden)
std::cout << " Too high!" << std::endl;
else if (guess < hidden)
std::cout << " Too low!" << std::endl;
}
std::cout << "Congratulations! You have found the hidden number!" << std::endl;
}
```
%% Cell type:markdown id: tags:
`std::cin` is a bit more tricky to use that the others, as the risk the operation fails is really higher. For instance, if you give a string in the code above it will become crazy and keep printing the same message "Too high!" or "Too low!" (be ready to restart the kernel...). The following code fixes this:
%% Cell type:code id: tags:
``` C++17
#include <random>
#include <iostream>
{
std::random_device rd; //Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
std::uniform_int_distribution<> dis(0, 100);
auto hidden = dis(gen);
int guess = -1;
while (guess != hidden)
{
do
{
if (!std::cin)
{
std::cin.clear(); // clear the states of std::cin, putting it back to `goodbit`.
std::cin.ignore(10000, '\n'); // clean-up what might remain in std::cin before using it again.
}
std::cout << "Find the value between 0 and 100: ";
std::cin >> guess;
} while (!std::cin);
if (guess > hidden)
std::cout << " Too high!" << std::endl;
else if (guess < hidden)
std::cout << " Too low!" << std::endl;
}
std::cout << "Congratulations! You have found the hidden number!" << std::endl;
}
```
%% Cell type:markdown id: tags:
If you want to learn more about `std::cin`, you might want to look at [this post](https://stackoverflow.com/questions/5131647/why-would-we-call-cin-clear-and-cin-ignore-after-reading-input) on StackOverflow.
If you need to use it extensively, you should look more deeply the behaviour of the bit flags (`goodbit`, `badbit`, `failbit`, `eofbit`).
%% Cell type:markdown id: tags:
## Input/output with files
The same syntax with operators `<<` and `>>` may be used to interact with files; the streams are built with `std::ofstream` for an output stream and `std::ifstream` for an input stream.
%% Cell type:code id: tags:
``` C++17
#include <fstream> // for std::ifstream and std::ofstream
#include <iostream>
{
std::ofstream out("File.tmp");
out << 5 << std::endl;
out << -7 << std::endl;
out << 9 << std::endl;
out.close(); // file is written on disk when closed; automatically done when `out` gets out of scope otherwise
std::ifstream in("File.tmp");
int value;
while (in >> value)
std::cout << value << std::endl;
}
```
%% Cell type:markdown id: tags:
### `getline()`
When reading a file, if you want to interpret it line by line you should also consider `getline()`; this function may get a thrid argument to choose which separator to use (`\n` by default).
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <fstream>
#include <string>
{
std::ifstream in("File.tmp"); // assumes previous cell has been played recently!
std::string line;
while (getline(in, line))
std::cout << line << std::endl;
}
```
%% Cell type:markdown id: tags:
## `ostream` and `istream`
If you want to devise a function that may take as argument either a `std::cout` or a `std::ofstream`, you should use a `std::ostream` (we'll study [later](/notebooks/2-ObjectProgramming/6-inheritance.ipynb) why this works but just take my word for now):
%% Cell type:code id: tags:
``` C++17
#include <iostream>
void PrintOnStream(std::ostream& out)
{
out << "Printed on the chosen stream!" << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
PrintOnStream(std::cout);
```
%% Cell type:code id: tags:
``` C++17
#include <string>
#include <fstream>
{
std::ofstream out("test_stream.txt");
PrintOnStream(out);
}
```
%% Cell type:code id: tags:
``` C++17
{
// Read the content of the line previously written.
std::ifstream in("test_stream.txt");
std::string line;
getline(in, line);
std::cout << line << std::endl;
}
```
%% Cell type:markdown id: tags:
## Conversion
Stream syntax was until C++ 11 the only way to convert:
- A string into a number with `std::istringstream`
- A number into a string with `std::ostringstream`; the `str()` method returns the content as a `std::string`.
%% Cell type:code id: tags:
``` C++17
#include <sstream> // for std::ostringstream and std::istringstream
#include <string>
{
std::string number_as_string = "657";
int number;
std::istringstream iconv(number_as_string);
iconv >> number;
std::cout << "Number + 1 = " << number + 1 << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
#include <sstream> // for std::ostringstream and std::istringstream
#include <string>
{
int number = 657;
std::ostringstream oconv;
oconv << "The number is " << number;
std::cout << oconv.str() << std::endl;
}
```
%% Cell type:markdown id: tags:
To reuse a `std::ostringstream`, you must set its content to an empty string with an overloaded `str()`:
%% Cell type:code id: tags:
``` C++17
#include <sstream> // for std::ostringstream and std::istringstream
#include <string>
{
int number = 657;
std::ostringstream oconv;
oconv << "The number is " << number;
std::cout << oconv.str() << std::endl;
oconv.str(""); // reset oconv
oconv << "My new content is now there!";
std::cout << oconv.str() << std::endl;
}
```
%% Cell type:markdown id: tags:
In C++ 11, `std::to_string()` was introduced and provide similar functionality:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int number = 657;
std::string oconv("The number is ");
oconv += std::to_string(number);
std::cout << oconv << std::endl;
}
```
%% Cell type:markdown id: tags:
## Formatting and manipulators
You may act upon the exact formatting of the output
I'll be honest: it's not what is the most refined tool in the C++ library, and you may long for the simplicity and power of something like Python (or even C `printf`, which is a mess under the hood...).
The difficulty is that some settings apply only to the next entry onto the stream (`width` here), while others change the behaviour permanently (until told otherwise of course, e.g. `precision` here). Here are few examples of these syntaxes:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
std::cout.setf(std::ios::showpos); // Add the `+` sign explicitly
std::cout.setf(std::ios::fixed, std::ios::floatfield); // use decimal notation only
std::cout.precision(2); // number of decimal digits
std::cout.width(8) ;
std::cout << 1.237 ;
std::cout.width(8) ;
std::cout << 100.1245 ;
std::cout.width(8) ;
std::cout << '\n' ;
std::cout.width(8) ;
std::cout << 1.5774e-2 ;
std::cout.width(8) ;
std::cout << 12. << '\n' ;
}
```
%% Cell type:markdown id: tags:
**Manipulators** provide a shorter syntax to add some of the properties as the `width` or the `precision`:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
std::cout.setf(std::ios::showpos); // Add the `+` sign explicitly
std::cout.setf(std::ios::fixed, std::ios::floatfield); // use decimal notation only
std::cout << std::setprecision(2) << std::setw(8) << 1.237;
std::cout << std::setw(8) << 100.1245;
std::cout << '\n';
std::cout << std::setw(8) << 1.5774e-2;
std::cout << std::setw(8) << 12. << '\n';
}
```
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018_
_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)_
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment