Commit ced2cd9a authored by CHAMONT David's avatar CHAMONT David
Browse files

Ready for Prace 2022 Day 1.

parent 19480907
//
// Modify the class Ref, and only this class, so that
// the call to f(make_ref(j)) modify the value of j,
// although f(i) does not modify i.
// The final display should be : 0 42
//
#include <iostream>
template < typename T >
class Ref
{
public :
Ref( T data ) { data_ = data ; }
void operator=( T data ) { data_ = data ; }
private :
T data_ ;
} ;
template < typename T >
Ref<T> make_ref( T & data )
{ return Ref<T>(data) ; }
template < typename T >
void f( T data )
{ data = 42 ; }
int main()
{
int i = 0, j = 0 ;
f(i) ;
f(make_ref(j)) ;
std::cout<<i<<" "<<j<<std::endl ;
return 0 ;
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"\n",
"# Flaws of floating-point computing\n",
"\n",
"Floating-point numbers can represent a very large range of numbers, from the smallest to the largest, similarly to scientific notation. They are the prefered types for scientific computing. Yet, one must be aware of the many **rounding errors** which are implied. "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"First, in order to check visually the accuracy of some calculations, let's increase to 18 the output stream precision (this is 6 by default)."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"#include <iostream>\n",
"std::cout.precision(18) ;"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"The floating-point types have a limited number of digits available for their internal representation. Many numbers, such as `1./3.`, cannot be represented exactly:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.333333333333333315\n"
]
}
],
"source": [
"std::cout << (1./3.) << std::endl ;"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Less intuitive, some very simple numbers (for humans) do not have an exact base-two representation:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.100000000000000006\n"
]
}
],
"source": [
"std::cout << 0.1 << std::endl ;"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0x1.999999999999ap-4\n"
]
}
],
"source": [
"#include <iomanip>\n",
"std::cout << std::hexfloat << 0.1 << std::endl ;"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Some simple operations may amass rounding errors, which complicates comparison of floating-point numbers:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0x1p+0\n",
"0x1.fffffffffffffp-1\n",
"numbers differ !\n"
]
}
],
"source": [
"double d1 = 1. ;\n",
"double d2 = .1+.1+.1+.1+.1+.1+.1+.1+.1+.1 ;\n",
"\n",
"std::cout << d1 << std::endl ;\n",
"std::cout << d2 << std::endl ;\n",
"\n",
"if (d1==d2)\n",
" { std::cout<<\"numbers are the same\"<<std::endl ; }\n",
"else\n",
" { std::cout<<\"numbers differ !\"<<std::endl ; }"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Good old-fashioned practice: epsilon\n",
"\n",
"When comparing some floating point numbers, always allow an epsilon difference."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"#include <cmath>\n",
"#include <limits>"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"bool compare( double val1, double val2 )\n",
" {\n",
" double epsilon = std::numeric_limits<double>::epsilon() ;\n",
" return (std::abs(val1-val2)<epsilon) ;\n",
" }"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"numbers are the same\n"
]
}
],
"source": [
"if (compare(1.,.1+.1+.1+.1+.1+.1+.1+.1+.1+.1 ))\n",
" { std::cout<<\"numbers are the same\"<<std::endl ; }\n",
"else\n",
" { std::cout<<\"numbers differ !\"<<std::endl ; }"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Good old-fashioned practice: make the precision adjustable"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"The first simplest step is to define a type alias and use it throughout the code:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"#include <iostream>\n",
"#include <cmath>\n",
"#include <limits>"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"typedef double real ;"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"bool compare( real val1, real val2 )\n",
" {\n",
" double epsilon = std::numeric_limits<real>::epsilon() ;\n",
" std::cout<<\"(~\"<<epsilon<<\") \" ;\n",
" return (std::abs(val1-val2)<epsilon) ;\n",
" }"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(~2.22045e-16) numbers are the same\n"
]
}
],
"source": [
"if (compare(1.,.1+.1+.1+.1+.1+.1+.1+.1+.1+.1 ))\n",
" { std::cout<<\"numbers are the same\"<<std::endl ; }\n",
"else\n",
" { std::cout<<\"numbers differ !\"<<std::endl ; }"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"If you wish to perform the same calculations in single precision, just set `typedef` to `float`, compile and run."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## More flexible (but difficult) approach : templates\n",
"\n",
"Make any computing function a template, with the floating type as parameter. This allows to mix different precisions within different steps of a scientific computing application."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"template< typename Real >\n",
"bool compare( Real val1, Real val2 )\n",
" {\n",
" double epsilon = std::numeric_limits<Real>::epsilon() ;\n",
" std::cout<<\"(~\"<<epsilon<<\") \" ;\n",
" return (std::abs(val1-val2)<epsilon) ;\n",
" }"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(~1.19209e-07) numbers are the same\n"
]
}
],
"source": [
"float f1 = 1. ;\n",
"float f2 = .1+.1+.1+.1+.1+.1+.1+.1+.1+.1 ;\n",
"\n",
"if (compare(f1,f2))\n",
" { std::cout<<\"numbers are the same\"<<std::endl ; }\n",
"else\n",
" { std::cout<<\"numbers differ !\"<<std::endl ; }"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(~1.19209e-07) numbers differ !\n"
]
}
],
"source": [
"float f1 = 1.f ;\n",
"float f2 = .1f+.1f+.1f+.1f+.1f+.1f+.1f+.1f+.1f+.1f ;\n",
"\n",
"if (compare(f1,f2))\n",
" { std::cout<<\"numbers are the same\"<<std::endl ; }\n",
"else\n",
" { std::cout<<\"numbers differ !\"<<std::endl ; }"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(~1.19209e-07) numbers are the same\n"
]
}
],
"source": [
"float f1 = 1.f ;\n",
"float f2 = 10.f*.1f ;\n",
"\n",
"if (compare(f1,f2))\n",
" { std::cout<<\"numbers are the same\"<<std::endl ; }\n",
"else\n",
" { std::cout<<\"numbers differ !\"<<std::endl ; }"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Conclusion\n",
"\n",
"Modern C++ will not bring any silver bullet for the rounding problems of floating point computing. You still have to rely on only some old-fashioned good practice, and externals tools that can help to locate greatest errors (CADNA, verificarlo, verrou)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Questions ?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## References\n",
"\n",
"* https://www.learncpp.com/cpp-tutorial/floating-point-numbers/\n",
"* [What Every Programmer Should Know About Floating-Point Arithmetic](https://floating-point-gui.de/)\n",
"* [IEEE-754 Floating-Point Conversion](https://babbage.cs.qc.cuny.edu/IEEE-754.old/Decimal.html)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"© *CNRS 2020* \n",
"*This document was created by David Chamont and translated by Olga Abramkina. It is available under the [Licence Creative Commons - Attribution - No commercial use - Shared under the conditions 4.0 International](http://creativecommons.org/licenses/by-nc-sa/4.0/)*"
]
}
],
"metadata": {
"celltoolbar": "Diaporama",
"kernelspec": {
"display_name": "C++17",
"language": "C++17",
"name": "xcpp17"
},
"language_info": {
"codemirror_mode": "text/x-c++src",
"file_extension": ".cpp",
"mimetype": "text/x-c++src",
"name": "c++",
"version": "17"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
This diff is collapsed.
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Templates"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Principles\n",
"* Automated copy-and-paste of functions or classes, with different types and integer constants.\n",
"* Specialization: one can define a specific implementations of the function/class for specific types and/or constants."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Benefits\n",
"* Make code generic, yet strongly typed.\n",
"* Fully processed at compile time.\n",
"* Zero cost at run time."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Drawbacks\n",
"* Increased compile time.\n",