Files
saselang-bible/annexes/A-numeric-conversions.md
2026-09-12 08:56:27 +02:00

15 KiB
Raw Blame History

Saselang — Annexe A — Matrice des conversions numériques

Version documentaire : 0.2.12. Statut : inventaire de conception Core, destiné à devenir normatif après validation.

Cette annexe est le listing exhaustif de travail des conversions numériques Core. Son objectif est d'énumérer le maximum de conversions numériques sémantiquement distinctes sans introduire d'alias ou de doublons inutiles.

A.1. Répartition des responsabilités

Les conversions numériques sont exposées comme capacités de l'API Core des primitives. Elles ne deviennent pas chacune une construction grammaticale du compilateur.

Bible langage / compilateur
    définit les règles de typage, d'appel, d'intrinsic, de target et de lowering

Core
    expose les membres numériques concrets
    ex. int32::tryToInt8(), float64::tryRoundToFloat32()

Annexe Core
    liste exhaustivement les opérations disponibles par paire de types

Compilateur / Sase IR / backend
    valide et abaisse l'opération Core sans que chaque nom devienne de la grammaire

Une capacité Core peut être conditionnée par un target ou une plateforme lorsque cela est réellement nécessaire. L'absence d'une capacité demandée doit être diagnostiquée à la compilation pour le target choisi, et non découverte tardivement dans le backend.

Les conversions numériques fondamentales entre primitives standard sont destinées à constituer le socle Core commun ; le mécanisme de capacités existe surtout pour les opérations qui ne peuvent raisonnablement pas être garanties partout.

A.2. Règle de non-redondance

Une variante n'existe que si elle apporte une sémantique observable différente de l'opération canonique déjà disponible pour la paire source/destination.

Exemple :

int8 value = ...;
int32 larger = value::toInt32();

Puisque toutes les valeurs int8 sont représentables exactement dans int32, les formes suivantes n'ont aucune raison d'exister :

tryToInt32()
saturateToInt32()
wrapToInt32()

En revanche, pour int32 -> int8, tryToInt8(), saturateToInt8() et wrapToInt8() ont trois contrats différents et peuvent coexister.

A.3. Nomenclature générale

Code Famille Contrat
T toTarget() Exacte et totale pour toutes les valeurs valides du type source.
E tryToTarget() Exacte pour la valeur courante ; retourne Err si l'exactitude ou la représentabilité échoue.
R roundToTarget() Perte de précision explicitement acceptée ; conversion totale pour cette paire de types.
TR tryRoundToTarget() Arrondi explicitement accepté, mais la valeur peut être hors du domaine destination.
S saturateToTarget() Conversion totale par saturation lorsqu'aucun arrondi supplémentaire n'est nécessaire.
SR saturatingRoundToTarget() Arrondi canonique + saturation explicites pour une destination flottante lorsque les deux peuvent être nécessaires.
W wrapToTarget() Conversion entière modulo 2^N, avec interprétation selon le type entier destination.
aucune Même type ou opération sans sémantique distincte utile.

NumericConversionError est retenu comme nom de travail Core. Il pourra être renommé avant stabilisation de la spécification si nécessaire.

Les formes try... retournent conceptuellement :

Result<Target, NumericConversionError>

Aucune de ces opérations n'autorise une conversion implicite entre deux variables déjà typées.

A.4. Flottants : hypothèses de représentation

Type Précision significative p Exposant maximal fini
float16 11 bits 15
float32 24 bits 127
float64 53 bits 1023
float128 113 bits 16383

Les conversions avec arrondi utilisent par défaut la règle canonique round to nearest, ties to even. Cette règle ne dépend ni du linter, ni du profil, ni du backend.

A.5. Entier -> entier

Légende :

  • T = toTarget() uniquement ;
  • E+S+W = tryToTarget() + saturateToTarget() + wrapToTarget().
Source \ Destination int8 int16 int32 int64 int128 int256 uint8 uint16 uint32 uint64 uint128 uint256
int8 T T T T T E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W
int16 E+S+W T T T T E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W
int32 E+S+W E+S+W T T T E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W
int64 E+S+W E+S+W E+S+W T T E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W
int128 E+S+W E+S+W E+S+W E+S+W T E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W
int256 E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W
uint8 E+S+W T T T T T T T T T T
uint16 E+S+W E+S+W T T T T E+S+W T T T T
uint32 E+S+W E+S+W E+S+W T T T E+S+W E+S+W T T T
uint64 E+S+W E+S+W E+S+W E+S+W T T E+S+W E+S+W E+S+W T T
uint128 E+S+W E+S+W E+S+W E+S+W E+S+W T E+S+W E+S+W E+S+W E+S+W T
uint256 E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W E+S+W

Règles :

  • si le domaine source est entièrement inclus dans le domaine destination, seule la forme to... existe ;
  • sinon tryTo... effectue une conversion exacte conditionnelle ;
  • saturateTo... borne à Target::Min / Target::Max ; pour un signé vers non signé, toute valeur négative sature à 0 ;
  • wrapTo... utilise une définition mathématique modulo 2^N, indépendante de la représentation machine du backend ;
  • aucun wrapping ni saturation n'est implicite.

A.6. Entier -> flottant

Légende :

  • T = toFloatXX() uniquement ;
  • E+R = tryToFloatXX() + roundToFloatXX() ;
  • E+TR+SR = tryToFloatXX() + tryRoundToFloatXX() + saturatingRoundToFloatXX().
Source \ Destination float16 float32 float64 float128
int8 T T T T
int16 E+R T T T
int32 E+TR+S E+R T T
int64 E+TR+S E+R E+R T
int128 E+TR+S E+R E+R E+R
int256 E+TR+S E+TR+S E+R E+R
uint8 T T T T
uint16 E+TR+S T T T
uint32 E+TR+S E+R T T
uint64 E+TR+S E+R E+R T
uint128 E+TR+S E+TR+S E+R E+R
uint256 E+TR+S E+TR+S E+R E+R

Contrats :

  • toFloatXX() existe seulement si toute valeur source est exactement représentable ;
  • tryToFloatXX() exige une représentation exacte de la valeur courante ;
  • roundToFloatXX() existe seulement lorsque toute valeur source reste dans le domaine fini destination, mais qu'une perte de précision peut être nécessaire ;
  • tryRoundToFloatXX() accepte l'arrondi canonique mais retourne Err si la magnitude finie dépasse le domaine destination ;
  • saturatingRoundToFloatXX() est réservé aux paires pour lesquelles un débordement de domaine est possible : une valeur finie trop grande est bornée au plus grand fini de même signe, puis la précision destination s'applique selon l'arrondi canonique ;
  • il n'existe pas de wrapToFloatXX().

Exemple distinct :

int64 value = ...;

Result<float64, NumericConversionError> exact = value::tryToFloat64();
float64 approximated = value::roundToFloat64();

tryToFloat64() et roundToFloat64() ne sont pas des alias : le premier refuse toute perte de précision, le second l'accepte explicitement.

A.7. Flottant -> flottant

Légende :

  • T = toFloatXX() uniquement ;
  • E+TR+SR = tryToFloatXX() + tryRoundToFloatXX() + saturatingRoundToFloatXX().
Source \ Destination float16 float32 float64 float128
float16 T T T
float32 E+TR+S T T
float64 E+TR+S E+TR+S T
float128 E+TR+S E+TR+S E+TR+S

Règles :

  • l'élargissement de format est exact au niveau de la valeur IEEE et utilise uniquement toFloatXX() ;
  • la réduction de format propose tryToFloatXX() pour exiger l'exactitude, tryRoundToFloatXX() pour accepter la perte de précision mais pas le débordement fini, et saturatingRoundToFloatXX() pour obtenir une opération totale sur le domaine flottant ;
  • NaN, +Infinity, -Infinity, +0 et -0 restent des catégories IEEE valides dans la destination ;
  • saturatingRoundToFloatXX() ne transforme pas une infinité en valeur finie puisque l'infinité est elle-même représentable dans le format destination ; la saturation explicite concerne les valeurs finies hors du domaine fini destination ;
  • la conservation exacte du payload binaire d'un NaN n'est pas garantie par une conversion de valeur ; elle relève d'un éventuel contrat binaire distinct et de bitcast lorsque les tailles sont compatibles.

A.8. Flottant -> entier : inventaire des politiques

Il n'existe jamais de simple toIntXX() ou toUintXX() depuis un flottant.

La conversion doit annoncer à la fois la politique de passage du réel à l'entier et, lorsque cela est pertinent, la politique de dépassement de domaine.

A.8.1. Politique mathématique

Famille Sens
Exact Accepte uniquement une valeur flottante finie déjà mathématiquement entière.
Floor Plus grand entier mathématique inférieur ou égal.
Ceil Plus petit entier mathématique supérieur ou égal.
Round Nearest, ties to even.
Truncate Suppression de la partie fractionnaire, donc direction zéro.

A.8.2. Politique de domaine destination

Politique Forme Comportement
Checked try<Policy>ToTarget() Err sur NaN, infinité ou résultat hors plage.
Saturating trySaturating<Policy>ToTarget() Err sur NaN; les infinités et résultats hors plage saturent aux bornes destination.
Wrapping tryWrapping<Policy>ToTarget() Err sur NaN et infinité; les résultats entiers finis sont réduits modulo 2^N.

La famille complète potentiellement distincte pour une destination entière donnée est donc :

value::tryExactToInt32()
value::tryFloorToInt32()
value::tryCeilToInt32()
value::tryRoundToInt32()
value::tryTruncateToInt32()

value::trySaturatingExactToInt32()
value::trySaturatingFloorToInt32()
value::trySaturatingCeilToInt32()
value::trySaturatingRoundToInt32()
value::trySaturatingTruncateToInt32()

value::tryWrappingExactToInt32()
value::tryWrappingFloorToInt32()
value::tryWrappingCeilToInt32()
value::tryWrappingRoundToInt32()
value::tryWrappingTruncateToInt32()

La même famille est applicable aux douze destinations entières int8..int256 / uint8..uint256 et aux quatre sources flottantes.

Cette section est volontairement exhaustive : lors de la stabilisation Core, certaines combinaisons pourront être supprimées si elles n'apportent aucune valeur pratique ou si une composition Core unique est préférée. Elles ne devront en revanche jamais être remplacées par une opération ambiguë.

A.8.3. Matrice flottant -> entier

Toutes les paires source/destination partagent actuellement le même ensemble de politiques candidates ; la table sert à garantir qu'aucun type n'est oublié.

Source \ Destination int8 int16 int32 int64 int128 int256 uint8 uint16 uint32 uint64 uint128 uint256
float16 F15 F15 F15 F15 F15 F15 F15 F15 F15 F15 F15 F15
float32 F15 F15 F15 F15 F15 F15 F15 F15 F15 F15 F15 F15
float64 F15 F15 F15 F15 F15 F15 F15 F15 F15 F15 F15 F15
float128 F15 F15 F15 F15 F15 F15 F15 F15 F15 F15 F15 F15

F15 désigne les 15 opérations candidates listées en A.8.2 : 5 politiques mathématiques × 3 politiques de domaine.

Cas particuliers :

  • +0 et -0 produisent l'entier zéro lorsque l'opération choisie réussit ;
  • NaN n'a jamais de conversion entière implicite ni de valeur saturée/wrappée arbitraire ;
  • le wrapping est défini sur le résultat entier mathématique fini après application de Exact/Floor/Ceil/Round/Truncate ;
  • pour une destination non signée, la saturation d'une valeur négative aboutit à 0 ;
  • pour une destination signée, la saturation utilise Target::Min / Target::Max.

A.9. bitcast reste hors de cette matrice

bitcast<T>(value) n'est pas une conversion numérique. Il conserve les bits et change leur interprétation sous les contraintes de taille et de validité déjà définies par le langage.

Il ne constitue jamais une alternative implicite à to..., try..., round..., saturate... ou wrap....

A.10. Typage contextuel des littéraux

Le typage contextuel d'un littéral reste distinct de toute conversion de valeur déjà typée.

int32 a = 42;                    // contextualisation du littéral

int8 small = ...;
int32 b = small;                // ERROR : pas de conversion implicite
int32 c = small::toInt32();     // OK

A.11. Core, targets et capacités

Les membres de conversion appartiennent au Core des primitives. Le compilateur doit pouvoir reconnaître leur contrat via les métadonnées/Core intrinsics nécessaires sans transformer chaque membre en syntaxe spéciale.

Une opération Core peut être :

  • universelle dans le profil Core minimal ;
  • disponible par émulation logicielle même sans instruction machine native ;
  • ou conditionnée par une capacité target lorsqu'elle dépend réellement d'une plateforme/backend.

Une optimisation matérielle ne constitue pas, à elle seule, une raison de rendre une opération absente : une implémentation logicielle conforme est préférable lorsqu'elle est raisonnable.

A.12. Hiérarchie documentaire cible

La documentation Saselang est destinée à être organisée par niveaux :

Language / Compiler Specification
    ce que le compilateur doit accepter, refuser et produire

Saselang + Core Specification
    langage + environnement Core normatif

Saselang Platform Documentation
    langage + Core + SDKs + extensions de plateforme

À partir de 1.0.0-alpha, la Bible sera distribuée sous forme d'une archive ZIP structurée avec sommaire, chapitres séparés, exemples/DO-DON'T par chapitre et annexes référencées.

A.13. Points encore à valider

Les points suivants restent volontairement ouverts avant de rendre cette annexe normative :

  1. confirmer la nomenclature exacte des formes composées trySaturating... et tryWrapping... pour float -> integer ;
  2. décider si les 15 opérations float -> integer doivent réellement être exposées directement ou si certaines doivent être obtenues par composition d'opérations Core sans créer d'alias sémantique ;
  3. préciser le contrat observable des payloads NaN lors des conversions float -> float ;
  4. définir les codes et informations minimales de NumericConversionError ;
  5. classer explicitement les capacités numériques en Core minimal obligatoire ou capacité conditionnelle de target.