359 lines
12 KiB
Markdown
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.
|