383 lines
17 KiB
Markdown
383 lines
17 KiB
Markdown
# 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.
|
|
|
|
```text
|
|
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 :
|
|
|
|
```saselang
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```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
|
|
|
|
| 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+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 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
|
|
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, 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.
|
|
|
|
## A.8. Flottant -> entier : composition des politiques
|
|
|
|
Il n'existe jamais de simple `toIntXX()` ou `toUintXX()` depuis un flottant.
|
|
|
|
La conversion stricte canonique est :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```saselang
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```saselang
|
|
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 :
|
|
|
|
```text
|
|
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 `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`.
|
|
|
|
## 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.
|
|
|
|
```saselang
|
|
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 :
|
|
|
|
```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
|
|
```
|
|
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
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` ;
|
|
4. les tests de conformité couvrant toutes les cellules de la matrice.
|