17 KiB
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.
Une règle de composition complète cette non-redondance :
Deux opérations ne sont fusionnées dans un même nom que si leur séparation en opérations Core successives modifierait la sémantique, perdrait de l'information ou empêcherait d'exprimer le même contrat.
Ainsi value::floor()::tryToInt32() rend inutile un alias tryFloorToInt32() si les deux formes sont strictement équivalentes.
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 modulo2^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+SR | E+R | T | T |
| int64 | E+TR+SR | E+R | E+R | T |
| int128 | E+TR+SR | E+R | E+R | E+R |
| int256 | E+TR+SR | E+TR+SR | E+R | E+R |
| uint8 | T | T | T | T |
| uint16 | E+TR+SR | T | T | T |
| uint32 | E+TR+SR | E+R | T | T |
| uint64 | E+TR+SR | E+R | E+R | T |
| uint128 | E+TR+SR | E+TR+SR | E+R | E+R |
| uint256 | E+TR+SR | E+TR+SR | 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 retourneErrsi 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+SR | — | T | T |
| float64 | E+TR+SR | E+TR+SR | — | T |
| float128 | E+TR+SR | E+TR+SR | E+TR+SR | — |
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, etsaturatingRoundToFloatXX()pour obtenir une opération totale sur le domaine flottant ; NaN,+Infinity,-Infinity,+0et-0restent des catégories IEEE valides dans la destination ;- pour
NaN, seule la propriété sémantiqueisNaN(destination) == trueest garantie ; payload, signe et quiet/signaling bit ne sont pas garantis par une conversion de valeur ; tryToFloatXX()accepteNaNet les infinities lorsque la destination est un type flottant Saselang, car ces catégories y sont représentables ;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 d'une représentation binaire relève d'un contrat binaire distinct et de
bitcastlorsque ses propres contraintes sont compatibles.
A.8. Flottant -> entier : composition des politiques
Il n'existe jamais de simple toIntXX() ou toUintXX() depuis un flottant.
La conversion stricte canonique est :
tryToIntXX()
tryToUintXX()
Elle réussit uniquement si la valeur flottante est finie, mathématiquement entière, dans le domaine de la destination et exactement représentable comme entier destination. Sinon elle retourne Err(NumericConversionError(...)).
A.8.1. Politiques mathématiques séparées
Les opérations Core :
floor()
ceil()
round()
truncate()
restent des opérations sur le flottant et définissent chacune une politique mathématique unique.
Elles se composent ensuite avec les conversions :
value::floor()::tryToInt32()
value::ceil()::tryToInt32()
value::round()::tryToInt32()
value::truncate()::tryToInt32()
Cette composition remplace les alias redondants tryFloorTo..., tryCeilTo..., tryRoundTo... et tryTruncateTo....
round() utilise la règle canonique nearest, ties to even.
A.8.2. Dépassement de domaine
Les politiques de dépassement restent séparées de la politique mathématique.
Pour chaque destination entière, le Core peut exposer :
tryToTarget()
trySaturateToTarget()
tryWrapToTarget()
avec les contrats suivants :
| Opération | Préconditions non liées à la plage | Politique de plage |
|---|---|---|
tryToTarget() |
valeur finie et mathématiquement entière | Err si hors plage |
trySaturateToTarget() |
valeur finie et mathématiquement entière | borne à Target::Min / Target::Max |
tryWrapToTarget() |
valeur finie et mathématiquement entière | modulo 2^N selon le type entier destination |
Les politiques se composent :
value::floor()::trySaturateToInt32()
value::round()::trySaturateToUint16()
value::truncate()::tryWrapToInt8()
value::ceil()::tryWrapToUint64()
Il n'existe pas de variantes combinées telles que trySaturatingFloorToInt32() ou tryWrappingRoundToInt32() lorsque la composition ci-dessus possède exactement la même sémantique.
A.8.3. Matrice flottant -> entier après élimination des doublons
Pour chaque paire, la matrice conserve uniquement les politiques dont le résultat peut réellement différer sur une valeur source admissible.
Légende :
C
tryToTarget() uniquement
CSW
tryToTarget()
trySaturateToTarget()
tryWrapToTarget()
| Source \ Destination | int8 | int16 | int32 | int64 | int128 | int256 | uint8 | uint16 | uint32 | uint64 | uint128 | uint256 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| float16 | CSW | CSW | C | C | C | C | CSW | CSW | CSW | CSW | CSW | CSW |
| float32 | CSW | CSW | CSW | CSW | CSW | C | CSW | CSW | CSW | CSW | CSW | CSW |
| float64 | CSW | CSW | CSW | CSW | CSW | CSW | CSW | CSW | CSW | CSW | CSW | CSW |
| float128 | CSW | CSW | CSW | CSW | CSW | CSW | CSW | CSW | CSW | CSW | CSW | CSW |
Justification :
- toutes les valeurs finies et mathématiquement entières de
float16tiennent dansint32et les entiers signés plus larges ; saturation et wrapping n'apportent donc rien pour ces paires ; - toutes les valeurs finies et mathématiquement entières de
float32tiennent dansint256, mais pas dansint128ou plus petit ; - pour toute destination non signée, les valeurs négatives rendent checked, saturation et wrapping distincts, même lorsque toute magnitude positive finie tient dans la destination ;
float64etfloat128disposent de valeurs finies dépassant le domaine de tous les entiers Saselang V1.
Cas particuliers :
+0et-0donnent l'entier zéro ;NaN,+Infinityet-Infinityéchouent dans ces familles car ils ne satisfont pas la précondition de valeur finie et mathématiquement entière ;- pour une destination non signée, la saturation d'une valeur négative entière donne
0; - pour une destination signée, la saturation utilise
Target::Min/Target::Max; - le wrapping est appliqué au résultat entier mathématique fini selon modulo
2^N.
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.
Pour les conversions fondamentales de cette matrice :
si les types source et destination sont supportés par le target, les opérations non redondantes définies par la matrice sont Core-required.
L'absence d'une instruction matérielle native ne rend pas l'opération optionnelle lorsqu'une émulation logicielle conforme est raisonnablement possible.
Un target réduit peut ne pas exposer un type fondamental donné. Dans ce cas, l'indisponibilité doit porter sur le type/capacité fondamentale, pas sur une sélection arbitraire de ses conversions.
La nomenclature générale des capabilities et leurs niveaux sont définis au chapitre 40.
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
Depuis 0.2.12, la Bible est distribuée sous forme d'une archive multifichier structurée avec sommaire, chapitres séparés, exemples/DO-DON'T par chapitre et annexes référencées.
A.13. NumericConversionError
NumericConversionError est le nom de travail du ResultError Core des conversions récupérables.
Les codes sémantiques V1 sont :
NotFinite
NotIntegral
OutOfRange
Inexact
L'erreur reste minimale et ne transporte pas automatiquement la valeur source, les types source/destination, le backend, un timestamp ou un mode d'arrondi.
A.14. État de l'annexe
Les principes sémantiques de la matrice sont désormais largement figés :
non-redondance par paire
composition plutôt qu'alias combinés
règles float -> integer
règles NaN / infinities / signed zero
NumericConversionError
disponibilité Core pour les types supportés
émulation logicielle lorsque raisonnable
Restent principalement à auditer lors de l'implémentation Core :
- le listing mécanique exhaustif des membres concrets exposés par chaque primitive ;
- les noms définitifs des constantes/membres Core associés ;
- la représentation interne exacte des codes
NumericConversionError; - les tests de conformité couvrant toutes les cellules de la matrice.