Files
saselang-bible/annexes/A-numeric-conversions.md
2026-09-13 10:20:16 +02:00

359 lines
12 KiB
Markdown

# Saselang — Annexe A — Matrice des conversions numériques
**Version documentaire : 0.2.17. Statut : inventaire de conception Core destiné à devenir normatif après validation.**
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 des capacités de l'API Core des primitives, pas des constructions grammaticales distinctes.
```text
Bible langage / compilateur
définit typage, contrats, faults, targets et lowering
Core
expose les membres numériques concrets
ex. int32::toInt8(), float64::roundToFloat32()
Annexe Core
liste les opérations disponibles par paire de types
Compilateur / Sase IR / backend
valide et abaisse les opérations Core
```
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
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 :
```text
int8 value = ...;
int32 larger = value::toInt32();
```
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
toInt8() // exact, peut fault OutOfRange
saturateToInt8()
wrapToInt8()
```
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 :
```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` | `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. |
Aucune de ces opérations n'autorise une conversion implicite entre deux valeurs déjà typées.
## 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 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.6. Entier -> entier
Légende :
```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 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 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 :
- 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.7. Entier -> flottant
Légende :
```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+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+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 |
Exemple :
```text
int64 value = ...;
float64 exact = value::toFloat64();
float64 approximated = value::roundToFloat64();
```
La première opération exige l'exactitude et peut produire `NumericConversionFault::Inexact`; la seconde accepte explicitement la perte de précision.
## A.8. Flottant -> flottant
Légende :
```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+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 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.9. Flottant -> entier : composition des politiques
La conversion stricte canonique est :
```text
toIntXX()
toUintXX()
```
avec :
```text
faults NumericConversionFault
```
lorsque la valeur peut être non finie, non entière ou hors plage.
### A.9.1. Politiques mathématiques séparées
```text
floor()
ceil()
round()
truncate()
```
restent des opérations sur le flottant. Elles se composent ensuite avec la conversion :
```text
value::floor()::toInt32()
value::ceil()::toInt32()
value::round()::toInt32()
value::truncate()::toInt32()
```
Aucun alias combiné n'est ajouté lorsqu'il serait strictement équivalent.
### A.9.2. Dépassement de domaine
Pour chaque destination entière, le Core peut exposer :
```text
toTarget()
saturateToTarget()
wrapToTarget()
```
| Opération | Préconditions hors plage | Politique de plage |
|---|---|---|
| `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` |
`NotFinite` et `NotIntegral` restent possibles pour les trois familles lorsqu'elles s'appliquent.
Exemples :
```text
value::floor()::saturateToInt32()
value::round()::saturateToUint16()
value::truncate()::wrapToInt8()
value::ceil()::wrapToUint64()
```
### A.9.3. Matrice flottant -> entier après élimination des doublons
Légende :
```text
C
toTarget() uniquement
CSW
toTarget()
saturateToTarget()
wrapToTarget()
```
| 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 |
Cas particuliers :
- `+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.10. `bitcast` reste hors de cette matrice
`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...`.
## 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.
```text
int32 a = 42;
int8 small = ...;
int32 b = small; // ERROR
int32 c = small::toInt32(); // OK
```
## A.12. `NumericConversionFault`
```text
NumericConversionFault extends Fault
```
Codes sémantiques V1 :
```text
NotFinite
NotIntegral
OutOfRange
Inexact
```
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.13. État de l'annexe
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
NumericConversionFault
Core-required 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 `NumericConversionFault` ;
4. les tests de conformité couvrant toutes les cellules de la matrice.