« Programmation/Abstraction » : différence entre les versions

Un livre de Wikilivres.
Contenu supprimé Contenu ajouté
Tavernier (discussion | contributions)
nuance
Tavernier (discussion | contributions)
m coquille
Ligne 129 : Ligne 129 :
En informatique la virgule est en réalité une « étiquette » que l'on place dans une suite de chiffres finis dont les valeurs sont absolues. 1,33333333333... devient donc 133333333333 et on déclare d'une manière ou d'une autre qu'après le premier chiffre se trouve le séparateur entre partie entière et fractionnée. La notion de subdivision disparait, on ne garde que les valeurs brutes de chaque chiffre composant le nombre, dans les limitations du matériel qui ne permettra jamais de stocker toutes les décimales d'un nombre irrationnel par exemple.
En informatique la virgule est en réalité une « étiquette » que l'on place dans une suite de chiffres finis dont les valeurs sont absolues. 1,33333333333... devient donc 133333333333 et on déclare d'une manière ou d'une autre qu'après le premier chiffre se trouve le séparateur entre partie entière et fractionnée. La notion de subdivision disparait, on ne garde que les valeurs brutes de chaque chiffre composant le nombre, dans les limitations du matériel qui ne permettra jamais de stocker toutes les décimales d'un nombre irrationnel par exemple.


Dans le cas d'un nombre à « virgule fixe » la virgule peut soit ne simplement pas être stockée en mémoire. C'est au programmeur seul qu'incombe la tâche de déclarer la présence d'une virgule entre deux chiffres de ce nombre. C'est le rôle notamment de l'indicateur « V » utilisé en [[COBOL]] par exemple. Ou bien le nombre peut être enregistrée sous une forme dite ''décimale codée binaire'' (ou DCB), qui décompose le nombre binaires en chiffres et leur associe à chacun soit un quartet en DCB condensé, soit un octet en DCB étendu. Sachant que 4 bits sont nécessaires pour représenter tous les chiffres décimaux, cela veut dire qu'en étandu 4 bits vides seront consommés pour chaque chiffre stocké. Il est donc rapidement apparu la nécessité de trouver un moyen moins gourmand en ressources
Dans le cas d'un nombre à « virgule fixe » la virgule peut soit ne simplement pas être stockée en mémoire. C'est au programmeur seul qu'incombe la tâche de déclarer la présence d'une virgule entre deux chiffres de ce nombre. C'est le rôle notamment de l'indicateur « V » utilisé en [[COBOL]] par exemple. Ou bien le nombre peut être enregistrée sous une forme dite ''décimale codée binaire'' (ou DCB), qui décompose le nombre binaires en chiffres et leur associe à chacun soit un quartet en DCB condensé, soit un octet en DCB étendu. Sachant que 4 bits sont nécessaires pour représenter tous les chiffres décimaux, cela veut dire qu'en étendu 4 bits vides seront consommés pour chaque chiffre stocké. Il est donc rapidement apparu la nécessité de trouver un moyen moins gourmand en ressources


Dans le cas d'un nombre à « virgule flottante » on considère tous les chiffres comme un seul nombre appelé ''mantisse'' qui sera converti en binaire et stocké dans un jeu de valeurs. La puissance à laquelle la base est élevée (grossièrement la position de la virgule), appelée ''exposant'', est stockée dans un autre jeu de valeurs. Enfin un dernier jeu de valeurs indique le signe du nombre (un simple bit qui caractérise un nombre positif à 0, négatif à 1). L'ordonnancement de ces jeux de valeur varie selon la norme IBM ou IEEE (la première stocke le signe puis l'exposant puis la mantisse ; la deuxième stocke l'exposant puis le signe puis la mantisse).
Dans le cas d'un nombre à « virgule flottante » on considère tous les chiffres comme un seul nombre appelé ''mantisse'' qui sera converti en binaire et stocké dans un jeu de valeurs. La puissance à laquelle la base est élevée (grossièrement la position de la virgule), appelée ''exposant'', est stockée dans un autre jeu de valeurs. Enfin un dernier jeu de valeurs indique le signe du nombre (un simple bit qui caractérise un nombre positif à 0, négatif à 1). L'ordonnancement de ces jeux de valeur varie selon la norme IBM ou IEEE (la première stocke le signe puis l'exposant puis la mantisse ; la deuxième stocke l'exposant puis le signe puis la mantisse).

Version du 12 mars 2008 à 21:28

Un programme à son niveau le plus fondamental est une suite de 0 et de 1, que le système interprète grâce au processeur qui effectue les opérations correspondantes : lecture et écriture de données (mémoire, fichier, ...), calculs, transfert de données, ...

Mais entre les composants électroniques qui sont au cœur du système et le mode de pensée du cerveau humain, il y a une succession de couches successives développées tout au long de l'histoire de l'informatique destinées en globalité à traduire nos pensées abstraites en instructions concrètes au niveau du système de manière plus fiable et plus intuitive.

Le bit

Le terme bit est la contraction du terme anglais binary digit (nombre binaire). En électronique numérique les signaux et le composants composants ne peuvent généralement se stabiliser que sur 2 états, habituellement 0 et 5 volts, auxquels on attribue arbitrairement la valeur 0 pour l'un et 1 pour l'autre. Ainsi, les composants informatiques rassemblent des millions de transistors qui ne peuvent fonctionner que selon une logique à deux états. Pour simplifier si on applique une tension de valeur binaire 1 à un transistor, celui-ci laisse passer le courant, si on lui applique une tension correspondant à la valeur binaire 0, il bloquera le courant. Le courant lui même si il véhicule une information est bi-stable (stable sur deux états).

De même pour la représentation et stockage de données, toute image, toute information doit d'abord être convertie en une succession de 0 et de 1 pour que l'information puisse circuler dans les systèmes électroniques d'abord, puis stockée ou rendue.

À faire...link={{{link}}}

notion de « poids du bit »

À faire...link={{{link}}}

notion de « mot »

Représentation des nombres

Historiquement on a utilisé les ordinateurs pour manipuler des nombres décimaux. Le bit étant lui même un nombre, la conversion entre un nombre décimal (en base 10) en un nombre binaire (en base 2) peut se faire avec des procédures relativement simples.

La notion de base arithmétique

Dans un système numérique « basé », chaque chiffre d'un nombre est multiplié par la valeur de sa base, valeur qui elle-même est mise à une puissance correspondant à sa position dans le nombre par rapport au séparateur « décimal » (la virgule selon la norme française, le point selon la norme britannique).

Ainsi le nombre en base 10

veut dire

c'est à dire

ou encore

Le binaire pur

Pour savoir à quoi correspond en base 2 on modifie la base de calcul d'abord.

Ainsi en binaire, voudrait (improprement) dire

Sauf que 7, 6, 5... ne sont pas des chiffres binaires à proprement parler. En décimal on augmente la rangée suivante lorsqu'on arrive à 9, en binaire on le fait dès qu'on arrive à 1, c'est à dire que (comprendre zéro en base 10) vaudra (zéro en base 2), vaudra , mais vaudra , vaudra , et ainsi de suite. À titre indicatif voici le tableau de correspondance en binaire des 9 chiffres décimaux :

Correspondance entre les chiffres décimaux et leur valeur en binaire
0 1 2 3 4 5 6 7 8 9
0 1 10 11 100 101 110 111 1000 1001

Mais un 7 à la deuxième rangée d'un nombre décimal est fondamentalement différent d'une quelconque équivalence binaire à 7 à la même rangée. Le 7 de la base 10 est le résultat de 70 groupements de 10, on voit donc que si le groupement n'était pas fait sur une base 10, on n'aurait tout simplement pas de 7 ici : traduire un chiffre « basé » en tant que tel n'a tout simplement pas de sens mathématiquement parlant (bien que cela puisse en avoir informatiquement parlant comme on le verra plus loin). Il faut au contraire savoir combien de groupements de 2 peut on faire avec 765, en suivant un décompte binaire. Le moyen le plus rapide est d'effectuer des divisions successives par 2 en retenant la présence d'une partie fractionnée.

Ainsi

finalement pour la partie entière. Mais comment représenter la partie fractionnée ?

La virgule idéale, fixe et flottante

Dans le meilleur des mondes la fraction est bien une subdivision d'une unité, où plus on monterait dans les rangées, plus la subdivision serait précise. À strictement parler on représenterait la partie fractionnaire suivant la même logique que la partie entière, c'est à dire qu'à chaque fois qu'on descend d'une rangée dans le nombre, on réduit la puissance.

Ainsi en base 10

veut dire donc ,

veut dire donc , ...

Et en base 2

veut dire donc ,

veut dire donc , ...

Ainsi pour convertir une partie fractionnée, on décompose la fraction en correspondances entre subdivisions décimales et binaires.

Correspondance entre nombres décimaux et leur valeur en binaire
Binaire 0,1 0,01 0,001 0,0001 0,00001 0,000001 ...
Équivalent décimal 0,5 0,25 0,125 0,0625 0,03125 0,015625 ...

Donc avec pour trouver le correspondance binaire de on y soustrait toutes les équivalences possibles en veillant à ne pas dépasser la valeur recherchée. Ainsi .

Une autre manière est de multiplier récursivement la partie fractionnée par 2 en reportant la partie entière résultante dans le nombre binaire.

Ainsi

  • 0,432 × 2 = 0,864 -> 0
  • 0,864 × 2 = 1,728 -> 1
  • 0,728 × 2 = 1,459 -> 1
  • 0,459 × 2 = 0,918 -> 0
  • 0,918 × 2 = 1,836 -> 1
  • 0,836 × 2 = 1,672 -> 1
  • ...

vaut donc en binaire approximativement 1011111101,011011... Il s'agit d'un nombre à fraction continue vu que ce que représente 765,432 en base 10 ne peut être représenté que par approximations successives en base 2.

On voit que pour la partie fractionnée des nombres, la notion de base est elle même biaisée. Par exemple si on voulait stocker la valeur réelle de en base 10, il faudrait théoriquement un disque dur infini pour contenir tous les chiffres de la fraction récurrente (0,333333). En base 3 la valeur réelle de serait 0,1.

Pour éviter de générer des approximations qui n'existaient pas dans une base précédente, on utilise donc d'autres procédés pour manipuler et stocker ce qui se trouve « après la virgule ». Dans un environnement binaire il est de toutes façons impossible de représenter autre chose qu'un 0 ou un 1, donc à fortiori la notion de virgule, de fraction, de signe est obligatoirement abstraite.

En informatique la virgule est en réalité une « étiquette » que l'on place dans une suite de chiffres finis dont les valeurs sont absolues. 1,33333333333... devient donc 133333333333 et on déclare d'une manière ou d'une autre qu'après le premier chiffre se trouve le séparateur entre partie entière et fractionnée. La notion de subdivision disparait, on ne garde que les valeurs brutes de chaque chiffre composant le nombre, dans les limitations du matériel qui ne permettra jamais de stocker toutes les décimales d'un nombre irrationnel par exemple.

Dans le cas d'un nombre à « virgule fixe » la virgule peut soit ne simplement pas être stockée en mémoire. C'est au programmeur seul qu'incombe la tâche de déclarer la présence d'une virgule entre deux chiffres de ce nombre. C'est le rôle notamment de l'indicateur « V » utilisé en COBOL par exemple. Ou bien le nombre peut être enregistrée sous une forme dite décimale codée binaire (ou DCB), qui décompose le nombre binaires en chiffres et leur associe à chacun soit un quartet en DCB condensé, soit un octet en DCB étendu. Sachant que 4 bits sont nécessaires pour représenter tous les chiffres décimaux, cela veut dire qu'en étendu 4 bits vides seront consommés pour chaque chiffre stocké. Il est donc rapidement apparu la nécessité de trouver un moyen moins gourmand en ressources

Dans le cas d'un nombre à « virgule flottante » on considère tous les chiffres comme un seul nombre appelé mantisse qui sera converti en binaire et stocké dans un jeu de valeurs. La puissance à laquelle la base est élevée (grossièrement la position de la virgule), appelée exposant, est stockée dans un autre jeu de valeurs. Enfin un dernier jeu de valeurs indique le signe du nombre (un simple bit qui caractérise un nombre positif à 0, négatif à 1). L'ordonnancement de ces jeux de valeur varie selon la norme IBM ou IEEE (la première stocke le signe puis l'exposant puis la mantisse ; la deuxième stocke l'exposant puis le signe puis la mantisse).

Ce mécanisme de séparation décimale est majoritairement utilisé par les langages de programmation et permet de stocker des nombres allant de à environ. Les mots-clés real en PASCAL, float en C par exemple sont utilisés pour demander un stockage sous cette forme.

Les valeurs signées

Comme dit précédemment les deux seules choses concrètes que peut représenter un système informatique sont les deux états stables qu'on fait correspondre à 0 et 1. La notion de signe doit donc également être abstraite.

On l'a vu, avec les nombres flottants le signe est stocké dans un jeu de valeurs dédié. En DCB on utilise soit un quartet supplémentaire dans le cas du condensé, soit les 4 bits vides de l'octet le moins significatif dans le cas de l'étendu. Le signe est codé 0000 pour le signe + et 1111 pour le signe - (ou l'inverse), mais plus souvent 1011 pour le + et 1101 pour le - : 1011 et 1101 valant respectivement B et D en hexadécimal, sachant que les valeurs 2B et 2D codent respectivement les caractères + et - en ASCII.

Mais le système DCB est extrêmement gourmand en ressources, si bien que lorsqu'on veut représenter le signe d'un entier naturel sans aller jusqu'à utiliser les ressources demandées par un nombre à virgule flottante, on réserve un seul bit au sein du mot pour représenter le signe, et tous les autres bits pour stocker la valeur absolue du nombre. On utilise par convention le bit de poids le plus fort et on considère que si il vaut 0 le nombre est positif, sinon le nombre est négatif.

Ainsi avec un octet

0 0 0 0 0 0 1 0

vaut 2 tandis que

1 0 0 0 0 0 1 0

vaut -2

Pour utiliser ou non ce mécanisme les langages à typage statique utilisent des mots clés ; unsigned et signed pour le C par exemple.

À faire...link={{{link}}}


correspondance entre mots clés.

  • représentation en complément
  • tableau récapitulatif entre répercussions concrètes de unsigned, double, int, float ... ?

Le système hexadécimal

Comme on l'a vu dans le tableau représentant les correspondances entre les chiffres décimaux et leur valeur en binaire toutes les combinaisons possibles de 4 bits ne sont pas présentes avec le système décimal (à savoir 1010, 1011, 1100, 1101, 1110 et 1111). C'est pour pallier ce manque qu'il a été crée le système hexadécimal afin de regrouper toutes les valeurs que peuvent prendre un quartet (groupe de 4 bits). Ainsi une plage de chiffres binaires découpées en blocs successifs de 4 bits peut devenir « lisible » tout en prenant moins de place à être représentée. Pour désigner les quartets manquants on a choisi arbitrairement les 6 premières lettres de l'alphabet.

Valeur binaire sur 4 bits Valeur décimale Valeur hexadécimale
0000 0 0
0001 1 1
0010 2 2
0011 3 3
0100 4 4
0101 5 5
0110 6 6
0111 7 7
1000 8 8
1001 9 9
1010 10 A
1011 11 B
1100 12 C
1101 13 D
1110 14 E
1111 15 F

Astuces de conversion entre bases

Le nombre d'états en fonction du nombre de bits est calculé par la formule 2n.

Le bit le plus à droite est de poids faible. Le bit Le plus à gauche est celui de poids fort.

Nombre binaire 1 1 1 1 1 1 1 1
Poids 27 = 128 26 = 64 25 = 32 24 = 16 23 = 8 22 = 4 21 = 2 20 = 1

Pour convertir un nombre binaire en décimal, il suffit d'additionner chaque poids des bits à 1.

Exemple :

Nombre binaire 1 0 0 1 0 1 0 1
Poids 27 = 128 26 = 64 25 = 32 24 = 16 23 = 8 22 = 4 21 = 2 20 = 1

De la base 2 (binaire) vers la base 10 (décimal) :

10010101 -> 1 * 128 + 0 * 64 + 0 * 32 + 1 * 16 + 0 * 8 + 1 * 4 + 0 * 2 + 1 * 1
         ->   128                     +   16           +   4           +   1
         ->   149

De la base 2 (binaire) vers la base 16 (hexadécimal) :

1001 0101 -> 95

La conversion de la base binaire vers la base hexadécimale est plus facile que vers la base décimale. C'est la raison pour laquelle la base hexadécimale est beaucoup plus utilisée que les autres.

Il est également possible d'utiliser la base 8 (23) c'est à dire la base octale, où les bits sont regroupés par 3 ; ou toute autre base puissance de 2 (base 64 ou 128 pour encoder les fichiers binaires dans un texte, base 32, ...).

Représentation des données

Avec 8 bits ( un octet ) on peut représenter 256 caractères différents (28) dont toutes les lettres de l'alphabet latin incluant majuscules, minuscules, quelques caractères accentués, les caractères de ponctuations et les caractères spéciaux ( ? ! , ; : / <> () [] {} µ % $ * - = + ^ @ # " | & ' € etc) ainsi que les caractères de contrôles (fin de ligne, espace, ...).

À faire...link={{{link}}}

rapide topo sur différences entre ASCII, ISO, UTF..;

À faire...link={{{link}}}

données compressées, vectorielles, matricielles, images, sons, animation...

Paradigmes

Un paradigme en programmation est la manière dont le programmeur conçoit son ouvrage pour que celui-ci corresponde à ses besoins. Un programme peut être conçu comme une simple suite d'instructions où le but est d'effectuer un traitement sur une ressource donnée en fonction d'un environnement donné. On parle alors de programmation impérative car le programme est une suite d'instructions précises fournies par l'auteur qui manipulent des objets simples comme des chiffres, des images ou des caractères.

Cependant les besoins de l'informatique ont amené la nécessité de pouvoir agir sur des objets plus complexes, comme le pointeur de la souris présente dans l'interface graphique des systèmes d'exploitation tels que Windows, MacOS ou Linux et de pouvoir par exemple agir dessus depuis plusieurs sources dans le cas d'un réseau ou d'agir dessus d'une manière qui dépende d'avantage de l'environnement crée par l'utilisateur lorsqu'il déplace la souris, appuie sur une touche du clavier… que de la succession d'évènements planifiées par le programmeur.

Ainsi ces différentes manières d'appréhender la programmation influent directement sur la manière dont la discipline est enseignée, et sur la manière dont les programmes sont conçus et fabriqués.

Code source

Le code source d'un programme est ce que le programmeur rédige avec ses propres moyens ou avec l'assistance d'un éditeur de programme. Il est soit compilé, soit interprété. Pour cette raison, le code doit être rédigé dans un langage qui soit à la fois compatible avec le compilateur ou l'interprêteur (respecter la syntaxe), mais également compréhensible par un être humain (lisibilité, présentation, commentaires), en utilisant les caractères familiers (lettres, chiffres, ponctuation) plutôt que des caractères étendus (caractères de contrôles, caractères spéciaux).

Technologies de langages

Il existe de très nombreux langages de programmation différents qui obéissent chacun à une syntaxe et à une logique propres. Certains langages sont conçus pour être accessibles et pédagogiques, d'autres pour être efficaces et fiables, d'autres pour être rapides à l'exécution. Selon le programme que l'on souhaite réaliser, le choix du langage peut être un critère décisif.

Langage machine

Il s'agit du niveau d'abstraction le plus bas que peut appréhender le programmeur, si on excepte les premiers ordinateurs où un groupe de personnes salariées modifiaient elles-mêmes le contexte électronique de l'ordinateur selon les instructions qu'on leur communiquait. Il s'agit d'une suite de bits? que comprend le processeur?. Par exemple l'équivalent binaire de la suite de chiffres telle que 12 003 peut être comprise par le processeur comme la douzième instruction parmi les actions préimplantées par le constructeur que peut effectuer le processeur appliquée à l'élément situé à l'adresse numéro 3 du support visé par le processeur. De fait chaque instruction en langage machine correspond à une et une seule action du processeur et de fait l'augmentation de l'abstraction des langages de programmation correspond à l'augmentation du nombre d'actions du processeur correspondant à une instruction.

Langages assemblés

Ce langage est une traduction mot à mot du langage machine en une suite d'instructions dont le premier mot est un mot mnémotechnique de l'opération effectuée, suivi des opérandes, c'est à dire des paramètres de l'opération. Ce langage est donc également spécifique à un modèle de processeur.

La traduction est assurée par un programme d'assemblage appelé un assembleur?.

Les langages assembleurs les plus évolués ont des fonctionnalités supplémentaires comme l'utilisation de labels pour les sauts, les adresses de variables, ce qui évite de calculer l'adresse exacte à utiliser ; l'utilisation de pseudo-instructions traduits en une séquence d'instructions équivalente, encore appelées macros? ; ou encore la définition de sections de code, de sections de données, afin d'organiser le programme en blocs spécialisés.

Langages de haut niveau

Les langages de haut niveau sont soit des langages compilés, soit des langages interprétés.

Technologies d'exécution

Selon le langage, le code source rédigé par le programmeur peut être exécuté de différentes manières par l'ordinateur.

Langages interprétés

Le code source d'un langage interprété est traduit en langage machine de manière éphémère lors de son exécution par un programme interpréteur qui lit le code source instruction après instruction et effectue la conversion à la volée. Un langage interprété est donc portable sur divers types de plateformes à condition qu'elles possèdent un interpréteur pour ce langage qui sache convertir le code source en langage machine compatible avec la plateforme.

Certains langages interprétés modernes (Java, C#, et les autres langages .Net) possèdent un semi-compilateur traduisant le programme en un langage plus proche des machines en général. C'est-à-dire un langage pour une machine virtuelle dont l'interprétation par une machine concrète demande moins d'effort que l'interprétation directe.

Langages semi-interprétés

Également appelés langages semi-compilés, ils se caractérisent par une étape de compilation rapide du code source produisant du bytecode qui est ensuite exécuté par l'interpréteur de manière plus rapide que si il avait à interpréter le code source directement. Ces langages autorisent des possibilités inédites comme la modification du code source à la volée lors de l'exécution du programme et généralement une très grande abstraction des contraintes imputées à la programmation système, en pâtissant de performances moindres en terme de vitesse par rapport aux langages compilés. Ce sont les langages apparus le plus récemment.

Langages compilés

Un langage compilé définit une syntaxe qui lui est propre et indépendante du processeur. Cette syntaxe peut permettre l'utilisation d'instructions plus complexes, de structures de code (condition, boucle), de structures de données complexes (tableaux, structures, listes, …) et peut fournir des types de données plus évolués (chaînes de caractères, dates, ...).

Un programme dans un tel langage nécessite une phase de compilation pour valider la syntaxe et effectuer la traduction en langage machine. Cette phase est assurée par un compilateur qui traduit le programme pour un ou plusieurs modèles de processeur particuliers.