This commit is contained in:
2026-09-13 10:20:16 +02:00
parent 019c8ad335
commit b72193d656
38 changed files with 1523 additions and 837 deletions

View File

@@ -1,32 +1,29 @@
# Saselang — Annexe A — Matrice des conversions numériques
**Version documentaire : 0.2.12. Statut : inventaire de conception Core, destiné à devenir normatif après validation.**
**Version documentaire : 0.2.17. 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.
Cette annexe inventorie les conversions numériques Core sémantiquement distinctes sans créer 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.
Les conversions numériques sont des capacités de l'API Core des primitives, pas des constructions grammaticales distinctes.
```text
Bible langage / compilateur
définit les règles de typage, d'appel, d'intrinsic, de target et de lowering
définit typage, contrats, faults, targets et lowering
Core
expose les membres numériques concrets
ex. int32::tryToInt8(), float64::tryRoundToFloat32()
ex. int32::toInt8(), float64::roundToFloat32()
Annexe Core
liste exhaustivement les opérations disponibles par paire de types
liste 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
valide et abaisse les opérations Core
```
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.
Si les types source et destination sont supportés par un target, les conversions fondamentales non redondantes sont Core-required. Une implémentation logicielle conforme est admise lorsqu'une instruction matérielle n'existe pas.
## A.2. Règle de non-redondance
@@ -34,68 +31,86 @@ Une variante n'existe que si elle apporte une sémantique observable différente
Exemple :
```saselang
```text
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 :
Toutes les valeurs `int8` sont exactement représentables dans `int32`; il n'existe donc pas de variantes inutiles `saturateToInt32()` ou `wrapToInt32()`.
Pour `int32 -> int8`, les trois contrats suivants sont distincts :
```text
tryToInt32()
saturateToInt32()
wrapToInt32()
toInt8() // exact, peut fault OutOfRange
saturateToInt8()
wrapToInt8()
```
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 :
Règle de composition :
> 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.
Ainsi :
## A.3. Nomenclature générale
```text
value::floor()::toInt32()
```
rend inutile un alias `floorToInt32()` si les deux formes sont strictement équivalentes.
## A.3. Canal d'échec V1
Les anciennes variantes `try... -> Result<T,NumericConversionError>` ne font plus partie de la matrice canonique uniquement pour transporter un échec de conversion.
La conversion directe retourne la destination attendue et peut déclarer :
```text
faults NumericConversionFault
```
lorsque sa précondition runtime n'est pas totale.
`NumericConversionFault` est unchecked et capturable. Une API spécialisée peut toujours choisir explicitement `Result` si son domaine veut représenter l'échec comme une valeur normale, mais cela ne crée pas une seconde famille automatique `try...` dans le Core numérique.
## A.4. 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. |
| `E` | `toTarget()` | Exacte pour la valeur courante ; `faults NumericConversionFault` si impossible. |
| `R` | `roundToTarget()` | Perte de précision explicitement acceptée ; totale pour cette paire. |
| `FR` | `roundToTarget()` | Arrondi accepté ; peut fault si la valeur est hors du domaine destination. |
| `S` | `saturateToTarget()` | Saturation explicite lorsqu'aucun arrondi supplémentaire n'est nécessaire. |
| `SR` | `saturatingRoundToTarget()` | Arrondi canonique + saturation explicites. |
| `W` | `wrapToTarget()` | Conversion entière modulo `2^N`. |
| `—` | 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.
Aucune de ces opérations n'autorise une conversion implicite entre deux valeurs déjà typées.
Les formes `try...` retournent conceptuellement :
```saselang
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
## A.5. 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.
Les conversions avec arrondi utilisent 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
## A.6. Entier -> entier
Légende :
- `T` = `toTarget()` uniquement ;
- `E+S+W` = `tryToTarget()` + `saturateToTarget()` + `wrapToTarget()`.
```text
T
toTarget() total
E+S+W
toTarget() faults OutOfRange
saturateToTarget()
wrapToTarget()
```
| Source \ Destination | int8 | int16 | int32 | int64 | int128 | int256 | uint8 | uint16 | uint32 | uint64 | uint128 | uint256 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -114,95 +129,104 @@ Légende :
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 ;
- domaine source entièrement inclus -> `to...` total ;
- sinon `to...` reste exact mais peut produire `OutOfRange` ;
- `saturateTo...` borne à `Target::Min` / `Target::Max`, avec `0` pour un négatif vers non signé ;
- `wrapTo...` utilise une définition mathématique modulo `2^N` ;
- aucun wrapping ni saturation n'est implicite.
## A.6. Entier -> flottant
## A.7. Entier -> flottant
Légende :
- `T` = `toFloatXX()` uniquement ;
- `E+R` = `tryToFloatXX()` + `roundToFloatXX()` ;
- `E+TR+SR` = `tryToFloatXX()` + `tryRoundToFloatXX()` + `saturatingRoundToFloatXX()`.
```text
T
toFloatXX() total et exact
E+R
toFloatXX() exact, peut fault Inexact
roundToFloatXX() total
E+FR+SR
toFloatXX() exact, peut fault Inexact/OutOfRange
roundToFloatXX() peut fault OutOfRange
saturatingRoundToFloatXX() total
```
| 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 |
| int32 | E+FR+SR | E+R | T | T |
| int64 | E+FR+SR | E+R | E+R | T |
| int128 | E+FR+SR | E+R | E+R | E+R |
| int256 | E+FR+SR | E+FR+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 |
| uint16 | E+FR+SR | T | T | T |
| uint32 | E+FR+SR | E+R | T | T |
| uint64 | E+FR+SR | E+R | E+R | T |
| uint128 | E+FR+SR | E+FR+SR | E+R | E+R |
| uint256 | E+FR+SR | E+FR+SR | E+R | E+R |
Contrats :
Exemple :
- `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 :
```saselang
```text
int64 value = ...;
Result<float64, NumericConversionError> exact = value::tryToFloat64();
float64 exact = value::toFloat64();
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.
La première opération exige l'exactitude et peut produire `NumericConversionFault::Inexact`; la seconde accepte explicitement la perte de précision.
## A.7. Flottant -> flottant
## A.8. Flottant -> flottant
Légende :
- `T` = `toFloatXX()` uniquement ;
- `E+TR+SR` = `tryToFloatXX()` + `tryRoundToFloatXX()` + `saturatingRoundToFloatXX()`.
```text
T
toFloatXX() total et exact
E+FR+SR
toFloatXX() exact, peut fault
roundToFloatXX() accepte l'arrondi, peut fault OutOfRange fini
saturatingRoundToFloatXX() total
```
| 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 | — |
| float32 | E+FR+SR | — | T | T |
| float64 | E+FR+SR | E+FR+SR | — | T |
| float128 | E+FR+SR | E+FR+SR | E+FR+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, 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 ;
- pour `NaN`, seule la propriété sémantique `isNaN(destination) == true` est garantie ; payload, signe et quiet/signaling bit ne sont pas garantis par une conversion de valeur ;
- `tryToFloatXX()` accepte `NaN` et 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 `bitcast` lorsque ses propres contraintes sont compatibles.
- l'élargissement de format utilise `toFloatXX()` total ;
- la réduction exacte utilise `toFloatXX()` avec fault si nécessaire ;
- `roundToFloatXX()` accepte la perte de précision mais pas un débordement fini ;
- `saturatingRoundToFloatXX()` sature seulement les valeurs finies hors domaine fini ;
- `NaN`, `+Infinity`, `-Infinity`, `+0` et `-0` restent des catégories valides ;
- pour `NaN`, seule la propriété sémantique `isNaN(destination) == true` est garantie.
## A.8. Flottant -> entier : composition des politiques
Il n'existe jamais de simple `toIntXX()` ou `toUintXX()` depuis un flottant.
## A.9. Flottant -> entier : composition des politiques
La conversion stricte canonique est :
```text
tryToIntXX()
tryToUintXX()
toIntXX()
toUintXX()
```
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(...))`.
avec :
### A.8.1. Politiques mathématiques séparées
```text
faults NumericConversionFault
```
Les opérations Core :
lorsque la valeur peut être non finie, non entière ou hors plage.
### A.9.1. Politiques mathématiques séparées
```text
floor()
@@ -211,67 +235,56 @@ round()
truncate()
```
restent des opérations sur le flottant et définissent chacune une politique mathématique unique.
restent des opérations sur le flottant. Elles se composent ensuite avec la conversion :
Elles se composent ensuite avec les conversions :
```saselang
value::floor()::tryToInt32()
value::ceil()::tryToInt32()
value::round()::tryToInt32()
value::truncate()::tryToInt32()
```text
value::floor()::toInt32()
value::ceil()::toInt32()
value::round()::toInt32()
value::truncate()::toInt32()
```
Cette composition remplace les alias redondants `tryFloorTo...`, `tryCeilTo...`, `tryRoundTo...` et `tryTruncateTo...`.
Aucun alias combiné n'est ajouté lorsqu'il serait strictement équivalent.
`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.
### A.9.2. Dépassement de domaine
Pour chaque destination entière, le Core peut exposer :
```text
tryToTarget()
trySaturateToTarget()
tryWrapToTarget()
toTarget()
saturateToTarget()
wrapToTarget()
```
avec les contrats suivants :
| Opération | Préconditions non liées à la plage | Politique de plage |
| Opération | Préconditions hors 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 |
| `toTarget()` | valeur finie et mathématiquement entière | `OutOfRange` si hors plage |
| `saturateToTarget()` | valeur finie et mathématiquement entière | borne à `Target::Min` / `Target::Max` |
| `wrapToTarget()` | valeur finie et mathématiquement entière | modulo `2^N` |
Les politiques se composent :
`NotFinite` et `NotIntegral` restent possibles pour les trois familles lorsqu'elles s'appliquent.
```saselang
value::floor()::trySaturateToInt32()
value::round()::trySaturateToUint16()
Exemples :
value::truncate()::tryWrapToInt8()
value::ceil()::tryWrapToUint64()
```text
value::floor()::saturateToInt32()
value::round()::saturateToUint16()
value::truncate()::wrapToInt8()
value::ceil()::wrapToUint64()
```
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.
### A.9.3. Matrice flottant -> entier après élimination des doublons
Légende :
```text
C
tryToTarget() uniquement
toTarget() uniquement
CSW
tryToTarget()
trySaturateToTarget()
tryWrapToTarget()
toTarget()
saturateToTarget()
wrapToTarget()
```
| Source \ Destination | int8 | int16 | int32 | int64 | int128 | int256 | uint8 | uint16 | uint32 | uint64 | uint128 | uint256 |
@@ -281,75 +294,37 @@ 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 `float16` tiennent dans `int32` et 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 `float32` tiennent dans `int256`, mais pas dans `int128` ou 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 ;
- `float64` et `float128` disposent de valeurs finies dépassant le domaine de tous les entiers Saselang V1.
Cas particuliers :
- `+0` et `-0` donnent l'entier zéro ;
- `NaN`, `+Infinity` et `-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`.
- `+0` et `-0` donnent zéro ;
- `NaN` et les infinities produisent `NotFinite` ;
- une valeur fractionnaire produit `NotIntegral` avant la politique de plage ;
- vers un non signé, la saturation d'une valeur négative entière donne `0` ;
- le wrapping s'applique à l'entier mathématique fini modulo `2^N`.
## A.9. `bitcast` reste hors de cette matrice
## A.10. `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.
`bitcast<T>(value)` conserve les bits et change leur interprétation sous ses propres contraintes. Il ne constitue jamais une alternative implicite à `to...`, `round...`, `saturate...` ou `wrap...`.
Il ne constitue jamais une alternative implicite à `to...`, `try...`, `round...`, `saturate...` ou `wrap...`.
## A.10. Typage contextuel des littéraux
## A.11. Typage contextuel des littéraux
Le typage contextuel d'un littéral reste distinct de toute conversion de valeur déjà typée.
```saselang
int32 a = 42; // contextualisation du littéral
```text
int32 a = 42;
int8 small = ...;
int32 b = small; // ERROR : pas de conversion implicite
int32 c = small::toInt32(); // OK
int32 b = small; // ERROR
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 :
## A.12. `NumericConversionFault`
```text
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
NumericConversionFault extends Fault
```
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 :
Codes sémantiques V1 :
```text
NotFinite
@@ -358,19 +333,20 @@ 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.
Le fault reste minimal 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
## A.13. État de l'annexe
Les principes sémantiques de la matrice sont désormais largement figés :
Principes largement figés :
```text
non-redondance par paire
composition plutôt qu'alias combinés
Fault direct à la place des anciens try... purement mécaniques
règles float -> integer
règles NaN / infinities / signed zero
NumericConversionError
disponibilité Core pour les types supportés
NumericConversionFault
Core-required pour les types supportés
émulation logicielle lorsque raisonnable
```
@@ -378,5 +354,5 @@ Restent principalement à auditer lors de l'implémentation Core :
1. le listing mécanique exhaustif des membres concrets exposés par chaque primitive ;
2. les noms définitifs des constantes/membres Core associés ;
3. la représentation interne exacte des codes `NumericConversionError` ;
3. la représentation interne exacte des codes `NumericConversionFault` ;
4. les tests de conformité couvrant toutes les cellules de la matrice.