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

309 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
## 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+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 :
```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+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 :
```saselang
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.
```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.
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 :
```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
```
À 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.