Section 2.3

Les nombres

Entiers et flottants, la taille en suffixe, le signe en préfixe — et la division, qui ne fait pas ce qu'on croit.

Archon a deux familles de nombres, et la frontière entre les deux se voit à l’œil nu : un littéral avec un point est un flottant, sans point c’est un entier. Cette différence change le résultat d’un calcul, et c’est le sujet de cette sous-section.

@entry(demarrer)
object MonProgramme {
    demarrer() {
        var total := 7;
        var parts := 3;

        Console.print(text: $"{total} / {parts} = {total / parts}, reste {total % parts}");
        Console.print(text: $"en flottants : {7.0 / 3.0}");
    }
}

Lisez la première ligne de sortie avant d’exécuter, puis exécutez. 7 / 3 = 2.

La division entière tronque

Ce n’est pas un arrondi, c’est une troncature : 7 / 3 vaut 2, pas 2,33 et pas 3. Diviser deux entiers rend un entier, parce que le résultat d’une opération entre deux entiers est un entier — la règle n’a pas d’exception, et le langage préfère la tenir plutôt que de changer de type dans votre dos.

Ce qui a été jeté n’est pas perdu pour autant : % le rend. 7 % 3 vaut 1, le reste. Les deux ensemble disent tout de la division : 7, c’est trois fois 2, et il reste 1.

Pour obtenir 2,33, il faut demander une division de flottants — d’où la seconde ligne du programme, 7.0 / 3.0. Le point change tout : deux flottants divisés rendent un flottant.

int, float, et la taille en suffixe

Un entier est un int, un flottant un float. La taille en bits est un suffixe optionnel, collé au nom :

var a: int := 5;        // la taille du mot machine
var b: int32 := 5;      // 32 bits, garantis, quelle que soit la machine
var c: int64 := 5;

var x: float := 3.14;   // même logique
var y: float64 := 3.14;

Les noms sont écrits en toutes lettres, pas en abrégé — int32, jamais i32. Et le suffixe ne s’écrit que quand la taille compte : une structure binaire, un format de fichier, un appel vers une bibliothèque écrite ailleurs. Quand vous voulez simplement « un entier », int suffit, et le compilateur prend la taille du mot machine.

Contrairement au int du C, ce choix est fixé une fois pour toutes : il ne changera pas d’une plateforme à l’autre sous vos pieds.

unsigned — un entier qui ne descend pas sous zéro

Un entier est signé par défaut : il peut être négatif. Quand une valeur ne le sera jamais — un compte, un rang, un octet —, le mot unsigned le dit, en préfixe, en toutes lettres lui aussi :

var compteur: unsigned int := 5;     // jamais négatif, taille du mot machine
var octet: unsigned int8 := 200;     // de 0 à 255

Ce n’est pas un int avec un plafond plus haut, c’est un autre type, dont les valeurs commencent à zéro — et le compilateur le tient. Un littéral qui n’y tient pas est refusé avant même d’exécuter ; var compteur: unsigned int := -1; ne passe pas :

error[T033]: the literal '-1' does not fit in 'unsigned int64', whose values run from 0 to 18446744073709551615

Et un calcul qui en sortirait s’arrête : 0 - 1 sur un non signé n’a nulle part où aller, le programme lève une OverflowError — une erreur du même genre que la division entière par zéro, juste en dessous. Autre règle à retenir : signé et non signé ne se mélangent pas. Additionner un int et un unsigned int est refusé, et c’est à vous de dire lequel des deux vous vouliez. Enfin, unsigned ne s’applique qu’à un entier — un unsigned float n’a pas de sens, et le compilateur le dit.

Diviser par zéro : une erreur chez les entiers, une valeur chez les flottants

var zero := 0;
Console.print(text: $"{7 / zero}");

Le programme s’arrête, en le disant :

uncaught error — DivideByZeroError : division by zero

C’est un choix, et il est cohérent avec le reste du langage : chez les entiers, une division par zéro n’a pas de résultat — aucun entier ne saurait le porter —, donc le programme n’en invente pas. Il ne rend ni zéro, ni un nombre au hasard : il lève une erreur, qu’un programme peut attraper s’il s’y attend. Comment l’attraper est le sujet de la section 8.

Chez les flottants, la réponse est l’inverse, et c’est la même raison qui la donne : eux ont de quoi porter le résultat.

7.0 / 0.0      // inf
-7.0 / 0.0     // -inf
0.0 / 0.0      // nan

inf est l’infini, nan le « pas un nombre » — deux valeurs que la norme des flottants, IEEE 754, définit, et qu’un float range comme il range 3.14. Le langage les écrit lui-même, toujours de la même façon, plutôt que de laisser la machine choisir son orthographe.

Retenez donc la règle à l’endroit : ce n’est pas le zéro qui lève, c’est le zéro entier.

À vous

  • Changez 7 en 9 et relisez : 9 / 3 = 3, reste 0. Le reste à zéro, c’est la division juste.
  • Mettez un point à un seul des deux nombres — 7.0 / 3 — et lisez ce que le compilateur répond. Mélanger un entier et un flottant ne passe pas en silence : il vous demande de dire que c’était voulu.
  • Remplacez parts par 0 et exécutez. L’erreur nomme ce qui s’est passé, et le programme s’arrête là plutôt que de continuer avec un résultat inventé.
  • Mettez ensuite un point aux deux nombres — 7.0 / 0.0 — et exécutez : cette fois rien ne s’arrête, et inf s’affiche.
  • Déclarez var compteur: unsigned int := 0; puis, sur la ligne suivante, compteur := compteur - 1;. Exécutez : il n’y a pas de −1 chez les non signés, et le programme s’arrête en le disant. Mettez ensuite -1 directement dans la déclaration : cette fois le refus vient avant l’exécution, et il nomme l’intervalle permis.