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

12 KiB

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.

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

toIntXX()
toUintXX()

avec :

faults NumericConversionFault

lorsque la valeur peut être non finie, non entière ou hors plage.

A.9.1. Politiques mathématiques séparées

floor()
ceil()
round()
truncate()

restent des opérations sur le flottant. Elles se composent ensuite avec la conversion :

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 :

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 :

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 :

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.

int32 a = 42;

int8 small = ...;
int32 b = small;            // ERROR
int32 c = small::toInt32(); // OK

A.12. NumericConversionFault

NumericConversionFault extends Fault

Codes sémantiques V1 :

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 :

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.