Introduction
Lorsqu'on parle de «types scalaires», on parle d'un type de données contenant uniquement qu'une seule donnée élémentaire. On ne retrouve pas à ce niveau de données complexes comme des structures par exemple. Les types scalaires sont définis en deux groupes : les types discrets et les types réels.
Les types discrets
Les types discrets se définit, comme, avec un ensemble de valeurs possibles et énumérables ainsi qu'avec nombre précis. Ainsi, il n'y a pas de nombre infini, mais uniquement des nombres finis. Dans ce type de données, on parle d'abord de nombres, mais le concept comprend également des valeurs de toute sorte. La plupart des langages contiennent donc les types discrets suivants :
- Les naturels (soit des nombres positifs uniquement dans un intervalle données)
- Les entiers (soit des nombres positifs ou négatif dans un intervalle données)
- Les types d'énumérations (comprenant par exemple les valeurs booléennes)
- Les caractères (soit un ou des caractères ASCII)
- Les énumérations (soit un ensemble de valeurs défini par l'utilisateur). Dans l'exemple suivant, on énumère des couleurs primaires, des jours de semaines et des grandes villes du Québec :
- Les intervalles (soit assez souvent basé sur des limites plus restrictives que des entiers, des caractères ou des énumérations). Dans l'exemple suivant, écrit en Pascal, on définit un intervalle de jour de semaine, un intervalle pour définir les caractères majuscules et un intervalle pour les nombres naturels :
Dans certains langages de programmation, comme le Pascal, les types discrets ne peuvent pas s'échanger des valeurs entre eux et nécessite une fonction de conversion comme «ORD» par exemple, tandis que des langages de programmation comme le C, eux, sont un peu trop permissif et tolèrent les mélanges de toutes sortes. La raison pour laquelle certains langages préfèrent obliger l'utilisateur à utiliser une fonction réside dans le fait que le programmeur doit indiquer consciemment qu'il demande de faire une exception sur une règle précise et qu'il ne s'agit pas d'une erreur de distraction. À l'inverse, un langage de programmation comme le C se base sur le fait que le programmeur est très vigilant à ce qu'il fait et qu'il ne fait donc jamais d'erreur !
Un des langages de programmation les plus poussés au niveau des types discrets, demeure le langage de programmation Pascal.
Les types réels
Les types réels, bien qu'aujourd'hui banal, dans la plupart des langages de programmation, cause énormément de problème. La raison de cette complexité tient du fait qu'un nombre réel est, par définition, dénombrable et potentiellement infini. Toutefois, l'ordinateur, dans ses mécanismes de base est tout le contraire, soit, sans aucune abstraction, précise, rigide et avec des exactitudes. On arrive dans avec un nouveau concept, lequel est l'approximation !
- Virgule flottante : L'une représentation d'un nombre réel, est la représentation selon le format de la virgule flottante, soit une composition d'un signe (S), d'un exposant (E) d'une mantisse. Dans cette représentation, on considère que la virgule de la décimale, est placée à gauche de la mantisse et que les bits de données de poids élevé du type de données sont à 1 sauf lorsque la valeur du type de données est à 0,0. Ainsi, la mantisse représente toujours un nombre situé dans l'intervalle de 0,5 à 1,0. Cet intervalle est appelé «valeur normalisée». Le nombre réel est donc une représentation de la formule de 2 x (M x 2E). Pour les valeurs négatives, il existe plusieurs techniques différentes de la représentée, mais en général il s'agit soit d'un bit réservé à signe ou d'intervalle réservé pour les nombres négatifs.
- Virgule fixe : Une autre représentation d'un nombre réel est la représentation en nombre à virgule fixe. Ce format se base sur le fait qu'on utilise un nombre entier représentant la partie fractionnaire d'un nombre et l'autre partie à la partie entière du nombre réel.
- BCD : Enfin, il existe également la représentation BCD, provenant de l'abréviation de l'anglicisme Binary Coded Decimal, laquelle se base sur une virgule fixe, mais la virgule est fournie séparément. Grâce à cette technique, il n'y a pas d'erreur d'arrondissement possible contrairement à la représentation de la virgule flottante ou de la virgule fixe. Le langage de programmation COBOL est le langage de programmation utilisant le plus ce type de données.
Ainsi, les types réels, ayant des contraintes machines, sont assez différentes des propriétés que l'on connait dans la science de la mathématique. En résumé, en informatique, (X / Y) x Y n'est pas forcément égal à X à cause de toute sorte de facteurs comme l'arrondissement par exemple.
Très peu de langages de programmation permettent de choisir différentes représentations de nombre réel, parmi les plus flexibles ont retrouve le langage de programmation Ada et Delphi.
Compatibilité et conversion de types
Le concept de compatibilité et conversion de types dans les langages de programmation, et en particulier dans le langage de programmation Pascal, est essentiel pour comprendre comment différents types de données interagissent lorsqu'ils sont utilisés ensemble dans des expressions, des affectations ou des appels de procédure. Voici une explication détaillée des règles et concepts mentionnés :
Compatibilité
La compatibilité entre types de données se réfère à la capacité de deux types à être utilisés ensemble dans une opération donnée, comme l'affectation de valeurs ou l'évaluation d'expressions. En Pascal, les règles de compatibilité sont strictes et visent à prévenir les erreurs dues à des conversions implicites de types pouvant entraîner des résultats inattendus.
- Affectation : Lorsqu'une variable est affectée à une valeur.
- Type de la variable : Le type de la variable (gauche de l'affectation) doit être compatible avec le type de l'expression (droite de l'affectation). Par exemple : Si la variable est de type réel, l'expression à droite peut être un entier ou un réel. Si la variable est de type entier ou intervalle d'entiers, l'expression doit être un entier ou appartenir au même intervalle. Si la variable est de type discret (comme un type énuméré), l'expression à droite doit être du même type discret ou un intervalle basé sur ce type.
Les langages de programmation fortement typés comme Pascal imposent ces contraintes pour garantir que les opérations effectuées sur les données soient sûres et sans ambiguïté.
Évaluation d'expressions
Lorsque des expressions sont évaluées, le type du résultat dépend des types des composants de l'expression. Par exemple :
- Si les composantes d'une expression sont des entiers, alors l'expression est évaluée comme un entier.
- Si l'expression mélange des types entiers et réels, elle sera évaluée comme un réel (avec une conversion implicite de l'entier en réel pour garantir une précision).
- Dans le cas d'un intervalle de même type, le résultat sera évalué selon ce type.
L'évaluation des expressions utilise des règles de compatibilité pour assurer que les types peuvent être utilisés ensemble, et dans certains cas, des conversions implicites sont faites (comme d'un entier vers un réel) pour permettre l'évaluation. Cependant, cette flexibilité est limitée par des règles strictes pour éviter les erreurs inattendues.
Comparaison et ambiguïtés
Les opérateurs de comparaison acceptent des paramètres de types compatibles au sens de l'affectation (comme l'égalité, les opérateurs de plus grand ou plus petit,...), et ils renvoient toujours un résultat de type booléen. Cela signifie que, tant que les deux opérandes de l'opération de comparaison sont de types compatibles, l'opération sera valide et renverra un vrai ou un faux.
Cependant, il peut y avoir des ambiguïtés dans certaines expressions complexes où plusieurs opérateurs sont utilisés sans parenthèses pour clarifier l'ordre d'évaluation. Par exemple :
Dans l'expression A = B and C = D, plusieurs interprétations sont possibles, ce qui peut conduire à des résultats inattendus ou à des erreurs. Par exemple :
- ((A = B) and C) = D impliquerait que C et D soient des booléens.
- A = (B and (C = D)) exigerait que A et B soient des booléens.
Ces ambiguïtés sont levées par l'utilisation de parenthèses explicites pour indiquer l'ordre de priorités des opérations ou par des règles de précédence spécifiées dans le langage de programmation, définissant dans quel ordre les opérations doivent être évaluées.
Passage de paramètres
Les règles de compatibilité pour le passage de paramètres à une procédure sont similaires à celles de l'affectation. Le paramètre formel de la procédure (la variable déclarée dans la définition de la procédure) doit être compatible avec le paramètre effectif (la valeur ou la variable passée à la procédure). Par exemple, si une procédure attend un entier comme paramètre, vous ne pouvez pas passer une valeur de type réel sans provoquer une erreur, à moins que des conversions explicites ne soient utilisées.
Dans tous ces cas, Pascal impose un contrôle strict des types pour garantir la sécurité et la fiabilité des programmes, tout en limitant les erreurs de conversion et de compatibilité.
Conversion de type
La conversion de type en Pascal, comme dans beaucoup de langages de programmation, consiste à changer un type de donnée en un autre type afin de permettre certaines opérations. Cependant, Pascal est plus strict que certains autres langages, car la plupart des conversions doivent être faites de manière explicite à l'aide de fonctions dédiées. Une exception notable dans Pascal est la conversion des entiers en réels, pouvant se faire implicitement lorsque cela est nécessaire, par exemple lors de calculs impliquant des nombres à virgule flottante. Parmi les fonctions prédéfinies permettant la conversion explicite, on trouve ord pour convertir un type discret en entier, chr pour convertir un entier en caractère, trunc et round pour convertir un réel en entier.
En comparaison, le langage Ada impose des règles encore plus strictes pour garantir la sécurité dans la gestion des types. Il distingue entre les sous-types et les types dérivés. Un sous-type est une restriction apportée à un type de base, et il reste compatible avec ce dernier. En revanche, un type dérivé est considéré comme un type totalement nouveau, et donc incompatible avec son type de base, même s'il partage les mêmes caractéristiques. Par exemple, un sous-type comme OneDigit, restant un entier à l'intervalle de 0 à 9, reste compatible avec le type entier. Cependant, un type dérivé comme Entier, bien qu'il soit basé sur le type entier, est incompatible avec ce dernier. Pour passer d'un type dérivé à son type de base ou vice-versa, une conversion explicite est nécessaire. Exemple :
L'intérêt de cette distinction entre types dérivés et sous-types, en particulier en Ada, réside dans la sécurité de programmation. Elle permet de prévenir des erreurs logiques qui pourraient survenir si l'on tentait d'effectuer des opérations sur des variables qui, bien qu'elles aient des types compatibles numériquement, ne sont pas logiquement compatibles. Par exemple, additionner une superficie à une longueur n'a pas de sens, bien qu'elles puissent toutes deux être représentées par des entiers. Cette approche stricte des conversions assure une plus grande robustesse du code. Modula-2, un autre langage de programmation proche de Pascal, suit des règles similaires, mais n'offre pas de conversion implicite des entiers vers les réels. Au lieu de cela, il exige l'utilisation d'une fonction comme float pour effectuer cette conversion.
Conclusion
Si l'on compare les types discrets et les types réels, dans le premier cas, ils sont facilement représentables au niveau matériel d'un microprocesseur et ne demandent aucun traitement spécifique pour être représentés, tandis que les nombres réels ne sont pas spécifiques à un microprocesseur ou à un langage de programmation, ils sont étroitement reliés au compilateur ou à l'interpréteur de langage de programmation. Il est donc impossible de convertir directement une valeur réelle en binaire d'une machine à l'autre, contrairement aux types discrets.