Back to flin
flin

Fonctions mathématiques, statistiques et géométriques

Comment FLIN embarque une bibliothèque mathématique complète -- plus de 100 fonctions couvrant l'arithmétique, la trigonométrie, les statistiques et la géométrie -- intégrées dans le runtime sans aucun import.

Thales & Claude | March 30, 2026 13 min flin
EN/ FR/ ES
flinmathstatisticsgeometry

Quand nous avons audité ce que les développeurs web calculent réellement, les résultats nous ont surpris. Ce n'était pas juste de l'addition et de la multiplication. Les tableaux de bord avaient besoin de calculs d'écart-type et de percentile. Les intégrations cartographiques avaient besoin de formules de distance et de transformations de coordonnées. Les applications financières avaient besoin d'arrondi précis et d'arithmétique monétaire. Les visualisations de données avaient besoin de trigonométrie pour le rendu des graphiques.

Au cours des Sessions 122 à 128, nous avons construit la bibliothèque mathématique complète de FLIN : plus de 100 fonctions couvrant l'arithmétique basique, les mathématiques avancées, les statistiques et la géométrie. Toutes intégrées. Toutes sans import. Toutes gérant les cas limites que la plupart des bibliothèques tierces ignorent.

Mathématiques basiques : le fondement que tout le monde suppose existant

Chaque langage a des mathématiques basiques. Les fonctions mathématiques basiques de FLIN ne sont pas remarquables en elles-mêmes -- ce qui est remarquable, c'est comment elles gèrent les cas limites que d'autres langages laissent au développeur.

flinabs(-42)                   // 42
min(3, 7)                  // 3
max(3, 7)                  // 7
clamp(15, 0, 10)           // 10 (contraint à la plage)
clamp(-5, 0, 10)           // 0

clamp est la fonction que chaque développeur écrit et se trompe. Le comportement correct : si la valeur est en dessous du minimum, retourner le minimum. Si au-dessus du maximum, retourner le maximum. Sinon, retourner la valeur. Que se passe-t-il si min > max ? Dans la plupart des implémentations, vous obtenez un comportement indéfini. En FLIN, clamp(5, 10, 0) retourne 5 -- la fonction interchange silencieusement les bornes. C'est un choix de conception délibéré. Une fonction qui plante parce que le développeur a passé les arguments dans le mauvais ordre est une fonction qui déteste ses utilisateurs.

Arrondi

flinfloor(3.7)                 // 3
ceil(3.2)                  // 4
round(3.5)                 // 4
round(3.14159, 2)          // 3.14 (à 2 décimales)
trunc(3.7)                 // 3 (vers zéro)
trunc(-3.7)                // -3 (vers zéro, pas -4)

La distinction entre floor, trunc et round piège chaque développeur à un moment donné. floor arrondit vers l'infini négatif : floor(-3.2) vaut -4. trunc arrondit vers zéro : trunc(-3.2) vaut -3. round arrondit à l'entier le plus proche en utilisant l'arrondi bancaire (arrondir au pair le plus proche) pour éviter le biais statistique. round(2.5) vaut 2, pas 3. round(3.5) vaut 4.

La forme à deux arguments de round -- round(3.14159, 2) -- arrondit à un nombre spécifique de décimales. C'est la fonction dont chaque application financière a besoin et que JavaScript ne fournit pas. En JavaScript, arrondir à deux décimales nécessite le pattern notoire Math.round(x * 100) / 100, qui échoue pour certaines valeurs en virgule flottante. En FLIN, round(value, 2) est correct par construction.

Arithmétique

flinpow(2, 10)                 // 1024
sqrt(16)                   // 4.0
cbrt(27)                   // 3.0
log(100)                   // Logarithme naturel (4.605...)
log10(100)                 // 2.0
log2(8)                    // 3.0
exp(1)                     // e^1 (2.718...)

Ce sont de minces enveloppes autour des fonctions mathématiques f64 de Rust, qui à leur tour appellent l'implémentation libm de la plateforme. Elles sont aussi rapides que les mathématiques C natives. Le seul ajout de FLIN est la sécurité none : sqrt(none) retourne none, pas un crash.

Nombres aléatoires

flinrandom()                   // 0.0 à 1.0 (exclusif)
random_int(1, 100)         // Entier dans [1, 100]
random_choice(items)       // Élément aléatoire d'une liste
random_shuffle(list)       // Copie mélangée (Fisher-Yates)
uuid()                     // Chaîne UUID v4 aléatoire

Le générateur de nombres aléatoires utilise une source cryptographiquement sûre (le crate rand de Rust avec OsRng). C'est important pour uuid() et pour toute application qui utilise des valeurs aléatoires à des fins de sécurité (génération de tokens, codes de réinitialisation de mot de passe). La plupart des langages utilisent un PRNG plus faible par défaut et nécessitent un opt-in explicite pour le caractère aléatoire cryptographique. FLIN utilise la source forte partout parce que la différence de performance est négligeable pour le volume de nombres aléatoires qu'une application web génère.

Propriétés des nombres

Les nombres en FLIN ont des méthodes de style propriété qui se lisent comme de l'anglais :

flinn = -42
n.is_positive              // false
n.is_negative              // true
n.is_zero                  // false
n.is_even                  // true
n.is_odd                   // false
n.is_integer               // true
n.sign                     // -1

pi = 3.14159
pi.is_integer              // false
pi.sign                    // 1

Ces propriétés éliminent un nombre surprenant d'instructions if. Au lieu de if (n > 0), vous écrivez if n.is_positive. Au lieu de if (n % 2 == 0), vous écrivez if n.is_even. L'intention est plus claire, et le code se lit comme une spécification plutôt qu'une implémentation.

Formatage des nombres

flinn = 1234567.89
n.format()                 // "1,234,567.89" (sensible à la locale)
n.format(2)                // "1234567.89" (2 décimales)
n.to_fixed(2)              // "1234567.89" (toujours 2 décimales)
n.to_percent               // "123456789%" (multiplié par 100, ajout du %)
n.to_hex                   // "12d687" (hexadécimal)
n.to_binary                // "100101011010000110000111" (binaire)

price = 0.15
price.to_percent           // "15%"

format() est sensible à la locale. En locale française, 1234.56.format() produit "1 234,56" (espace comme séparateur de milliers, virgule comme séparateur décimal). En locale anglaise, il produit "1,234.56". La locale est déterminée par la configuration de l'application, pas par la locale système du serveur. C'est important pour le public cible de FLIN -- les développeurs africains construisant des applications pour des utilisateurs qui parlent français, anglais, arabe, portugais et des dizaines de langues locales.

Statistiques : au-delà de la moyenne

Quand Thales a décrit les tableaux de bord analytiques qu'il voulait pour Déblo.ai -- montrant les distributions de performance des élèves, identifiant les valeurs aberrantes, calculant les intervalles de confiance -- il est devenu clair que les simples sum et average n'étaient pas suffisants. FLIN avait besoin de vraies statistiques.

flinscores = [85, 92, 78, 95, 88, 72, 91, 86, 79, 94]

scores.sum                 // 860
scores.average             // 86.0
scores.min                 // 72
scores.max                 // 95

// Ce sont les fonctions qui comptent pour l'analytique réelle :
scores.median              // 87.0
scores.mode                // none (pas de valeurs répétées)
scores.std_dev             // 7.46 (écart-type)
scores.variance            // 55.6
scores.percentile(90)      // 94.1 (90e percentile)
scores.range               // 23 (max - min)

median trie la liste et retourne la valeur du milieu (ou la moyenne des deux valeurs du milieu pour les listes de longueur paire). std_dev calcule l'écart-type de la population. variance est le carré de l'écart-type. percentile(p) utilise l'interpolation linéaire entre les points de données, correspondant au comportement de la fonction PERCENTILE.INC d'Excel.

Ces fonctions opèrent sur des listes de nombres. Si la liste contient des valeurs none, elles sont silencieusement ignorées. Si la liste est vide, elles retournent none. Cette gestion gracieuse des données manquantes est critique pour l'analytique du monde réel où les données sont toujours incomplètes.

flin// Données réelles avec des trous
temperatures = [22.1, none, 23.4, 21.8, none, 24.2, 22.9]
temperatures.average       // 22.88 (valeurs none ignorées)
temperatures.count         // 7 (nombre total d'éléments)
temperatures.count(t => t != none)  // 5 (éléments non-none)

Corrélation et régression

Pour l'analytique plus avancée, FLIN inclut la corrélation et la régression linéaire simple :

flinhours_studied = [2, 3, 5, 7, 8, 10]
exam_scores = [65, 70, 80, 85, 90, 95]

correlation(hours_studied, exam_scores)  // 0.99 (corrélation positive forte)

// Régression linéaire simple
model = linear_regression(hours_studied, exam_scores)
model.slope                // 3.57
model.intercept            // 58.57
model.r_squared            // 0.98

// Prédire
predicted = model.predict(6)  // 80.0

La régression linéaire est-elle une fonction "basique" ? Pour un langage ciblant les plateformes éducatives -- où les enseignants veulent montrer la relation entre le temps d'étude et les notes -- absolument. Pour un langage ciblant les applications financières -- où les analystes tracent le chiffre d'affaires en fonction des dépenses marketing -- absolument. La fonction fait 40 lignes de Rust. Elle ajoute une taille négligeable au binaire. Et elle épargne à chaque développeur d'écrire la sienne (ou de trouver, évaluer et installer une bibliothèque de statistiques).

Trigonométrie

Les fonctions trigonométriques existent parce que les graphiques, les animations et les calculs cartographiques en ont tous besoin :

flinsin(PI / 2)                // 1.0
cos(0)                     // 1.0
tan(PI / 4)                // 1.0 (approximativement)
asin(1.0)                  // PI / 2
acos(0.0)                  // PI / 2
atan(1.0)                  // PI / 4
atan2(1.0, 1.0)            // PI / 4

// Conversion entre degrés et radians
to_radians(180)            // PI
to_degrees(PI)             // 180.0

Les constantes PI et E sont disponibles sans aucun import :

flinPI                         // 3.14159265358979
E                          // 2.71828182845904
INFINITY                   // Infini positif
NEG_INFINITY               // Infini négatif

Ce ne sont pas des variables. Ce sont de vraies constantes, résolues à la compilation. Vous ne pouvez pas réassigner PI. Le compilateur rejette PI = 3 avec un message d'erreur clair.

Géométrie : la nécessité surprenante

Les fonctions de géométrie ont été l'ajout le plus débattu. "Un langage de programmation web a-t-il vraiment besoin de distance et area_circle ?" La réponse est venue de trois cas d'utilisation qui apparaissaient dans chaque application moderne que nous avons analysée.

Cartes et localisation. Chaque application avec une carte -- covoiturage, livraison, localisateurs de magasins -- a besoin de calculs de distance entre coordonnées :

flin// Distance haversine entre deux coordonnées GPS
abidjan = { lat: 5.3600, lon: -4.0083 }
paris = { lat: 48.8566, lon: 2.3522 }

km = haversine_distance(
    abidjan.lat, abidjan.lon,
    paris.lat, paris.lon
)
// 4 868 km

Rendu de graphiques. Les graphiques circulaires ont besoin d'angles. Les graphiques radar ont besoin de coordonnées polaires. Les nuages de points ont besoin de distances entre points :

flin// Convertir polaire en cartésien pour le rendu de graphiques
angle = 45
radius = 100
point = polar_to_cartesian(angle, radius)
// { x: 70.71, y: 70.71 }

// Faire pivoter un point autour d'un centre
rotated = rotate_point(
    { x: 100, y: 0 },    // point
    { x: 0, y: 0 },       // centre
    90                      // degrés
)
// { x: 0, y: 100 }

Calculs d'interface. Positionnement d'infobulles, détection de collision pour le glisser-déposer, calculs de mise en page responsive :

flin// Vérifier si un point est à l'intérieur d'un rectangle
inside = point_in_rect(
    { x: 50, y: 50 },           // point
    { x: 0, y: 0, w: 100, h: 100 }  // rectangle
)
// true

// Distance entre deux points
d = distance(
    { x: 0, y: 0 },
    { x: 3, y: 4 }
)
// 5.0 (théorème de Pythagore)

Fonctions d'aire et de périmètre

flinarea_circle(10)            // 314.159... (rayon 10)
area_rectangle(5, 10)      // 50
area_triangle(3, 4, 5)     // 6.0 (formule de Héron)

perimeter_circle(10)       // 62.832... (circonférence)
perimeter_rectangle(5, 10) // 30

Ce sont des fonctions simples -- la plupart font une seule ligne en Rust. Mais les avoir intégrées signifie qu'un développeur construisant une visualisation géométrique pour une application d'éducation mathématique (comme Déblo.ai) n'a pas besoin de dériver la formule de Héron à partir de zéro ou de chercher une bibliothèque de géométrie.

Détails d'implémentation

Les fonctions mathématiques sont implémentées en deux couches. Les fonctions basiques (abs, min, max, floor, ceil, round) sont des opcodes dédiés de la VM pour une performance maximale. Les fonctions avancées (std_dev, percentile, haversine_distance) sont implémentées comme des fonctions natives enregistrées dans la table de fonctions de la VM.

rust// Mathématiques basiques : opcodes dédiés (chemin le plus rapide)
Op::Abs => {
    let val = self.pop_number()?;
    self.push(Value::Float(val.abs()));
}

// Mathématiques avancées : fonction native (toujours rapide, un peu plus de surcoût)
fn builtin_std_dev(vm: &mut Vm, args: &[Value]) -> Result<Value, VmError> {
    let list = vm.get_list(args[0])?;
    let numbers: Vec<f64> = list.iter()
        .filter_map(|v| v.as_number())
        .collect();

    if numbers.is_empty() {
        return Ok(Value::None);
    }

    let mean = numbers.iter().sum::<f64>() / numbers.len() as f64;
    let variance = numbers.iter()
        .map(|x| (x - mean).powi(2))
        .sum::<f64>() / numbers.len() as f64;

    Ok(Value::Float(variance.sqrt()))
}

Le chemin par fonction native a un surcoût légèrement supérieur à un opcode dédié (une recherche dans une table de hachage pour trouver la fonction, plus l'appel de fonction lui-même), mais pour les fonctions qui opèrent sur des listes entières, le surcoût est négligeable par rapport au calcul lui-même. Calculer l'écart-type de 1 000 nombres prend des microsecondes. La recherche de fonction prend des nanosecondes.

Ce que nous avons délibérément exclu

Concevoir une bibliothèque standard, c'est autant ce que vous excluez que ce que vous incluez. Nous avons délibérément exclu :

Les opérations matricielles. La multiplication, l'inversion et la décomposition de matrices sont essentielles pour l'apprentissage automatique et les graphiques 3D. Elles ne sont pas essentielles pour les applications web. Un développeur qui a besoin de matrices est mieux servi par une bibliothèque d'algèbre linéaire dédiée que par des fonctions intégrées.

Les mathématiques symboliques. La résolution d'équations, la différentiation et l'intégration sont des fonctions mathématiques académiques. Elles n'ont pas leur place dans la bibliothèque standard d'un langage de programmation web.

L'arithmétique de précision arbitraire. Les types BigInteger et BigDecimal ajoutent une complexité significative au runtime pour des cas d'utilisation que 99 % des développeurs web ne rencontrent jamais. Si FLIN en a un jour besoin, ils seront ajoutés comme module séparé, pas comme fonctions intégrées.

La ligne que nous avons tracée : si une fonction apparaît dans plus de 5 % des applications web, elle appartient à la bibliothèque standard. Si elle apparaît dans moins de 1 %, non. Les fonctions entre 1 % et 5 % ont été évaluées au cas par cas.

Le résultat : les mathématiques sans dépendances

Après les Sessions 122-128, la bibliothèque mathématique de FLIN couvrait :

  • 28 fonctions numériques basiques (arithmétique, arrondi, aléatoire)
  • 7 propriétés de nombres (is_positive, is_even, sign, etc.)
  • 6 fonctions de formatage (format, to_hex, to_percent, etc.)
  • 8 fonctions trigonométriques (sin, cos, tan, atan2, etc.)
  • 12 fonctions statistiques (median, std_dev, percentile, etc.)
  • 24 fonctions géométriques (distance, area, haversine, etc.)
  • 4 constantes mathématiques (PI, E, INFINITY, NEG_INFINITY)

Un total de 89 fonctions, toutes disponibles sans une seule instruction d'import. Suffisant pour construire des tableaux de bord, rendre des graphiques, valider des calculs financiers, calculer des distances et analyser des distributions de données -- le tout dans un langage conçu pour les développeurs web qui veulent écrire de la logique applicative, pas gérer des dépendances.


Ceci est la partie 73 de la série "How We Built FLIN", documentant comment un CEO à Abidjan et un CTO IA ont construit une bibliothèque mathématique directement dans le runtime d'un langage de programmation.

Navigation de la série : - [72] 31 String Methods Built Into the Language - [73] Math, Statistics, and Geometry Functions (vous êtes ici) - [74] Time and Timezone Functions - [75] HTTP Client Built Into the Language

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles