Back to flin
flin

Funciones matemáticas, estadísticas y de geometría

Cómo FLIN incluye una biblioteca matemática completa -- más de 100 funciones que cubren aritmética, trigonometría, estadísticas y geometría -- integradas en el runtime del lenguaje sin importaciones.

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

Cuando auditamos lo que realmente calculan los desarrolladores web, los resultados nos sorprendieron. No era solo suma y multiplicación. Los paneles de control necesitaban cálculos de desviación estándar y percentiles. Las integraciones de mapas necesitaban fórmulas de distancia y transformaciones de coordenadas. Las aplicaciones financieras necesitaban redondeo preciso y aritmética de divisas. Las visualizaciones de datos necesitaban trigonometría para el renderizado de gráficos.

A lo largo de las Sesiones 122 a 128, construimos la biblioteca matemática completa de FLIN: más de 100 funciones que abarcan aritmética básica, matemáticas avanzadas, estadísticas y geometría. Todas integradas. Todas sin importaciones. Todas manejando casos extremos que la mayoría de las bibliotecas de terceros ignoran.

Matemáticas básicas: la base que todos asumen que existe

Todo lenguaje tiene matemáticas básicas. Las funciones básicas de FLIN no son notables en sí mismas -- lo notable es cómo manejan los casos extremos que otros lenguajes delegan al desarrollador.

flinabs(-42)                   // 42
min(3, 7)                  // 3
max(3, 7)                  // 7
clamp(15, 0, 10)           // 10 (restringido al rango)
clamp(-5, 0, 10)           // 0

clamp es la función que todo desarrollador escribe y se equivoca. El comportamiento correcto: si el valor está por debajo del mínimo, devolver el mínimo. Si está por encima del máximo, devolver el máximo. De lo contrario, devolver el valor. ¿Qué pasa si min > max? En la mayoría de las implementaciones, obtienes comportamiento indefinido. En FLIN, clamp(5, 10, 0) devuelve 5 -- la función intercambia los límites silenciosamente. Esta es una decisión de diseño deliberada. Una función que falla porque el desarrollador pasó los argumentos en el orden incorrecto es una función que odia a sus usuarios.

Redondeo

flinfloor(3.7)                 // 3
ceil(3.2)                  // 4
round(3.5)                 // 4
round(3.14159, 2)          // 3.14 (a 2 decimales)
trunc(3.7)                 // 3 (hacia cero)
trunc(-3.7)                // -3 (hacia cero, no -4)

La distinción entre floor, trunc y round confunde a todo desarrollador en algún momento. floor redondea hacia el infinito negativo: floor(-3.2) es -4. trunc redondea hacia cero: trunc(-3.2) es -3. round redondea al entero más cercano usando el redondeo bancario (redondear mitad al par) para evitar sesgo estadístico. round(2.5) es 2, no 3. round(3.5) es 4.

La forma de dos argumentos de round -- round(3.14159, 2) -- redondea a un número específico de decimales. Esta es la función que toda aplicación financiera necesita y que JavaScript no proporciona. En JavaScript, redondear a dos decimales requiere el infame patrón Math.round(x * 100) / 100, que falla para ciertos valores de punto flotante. En FLIN, round(value, 2) es correcto por construcción.

Aritmética

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

Estos son envoltorios delgados alrededor de las funciones matemáticas f64 de Rust, que a su vez llaman a la implementación libm de la plataforma. Son tan rápidos como las matemáticas nativas de C. La única adición que FLIN hace es la seguridad ante none: sqrt(none) devuelve none, no un error.

Números aleatorios

flinrandom()                   // 0.0 a 1.0 (exclusivo)
random_int(1, 100)         // Entero en [1, 100]
random_choice(items)       // Elemento aleatorio de la lista
random_shuffle(list)       // Copia barajada (Fisher-Yates)
uuid()                     // Cadena UUID v4 aleatoria

El generador de números aleatorios usa una fuente criptográficamente segura (el crate rand de Rust con OsRng). Esto importa para uuid() y para cualquier aplicación que use valores aleatorios con fines de seguridad (generación de tokens, códigos de restablecimiento de contraseña). La mayoría de los lenguajes usan un PRNG más débil por defecto y requieren una opción explícita para aleatoriedad criptográfica. FLIN usa la fuente fuerte en todas partes porque la diferencia de rendimiento es insignificante para el volumen de números aleatorios que genera una aplicación web.

Propiedades de números

Los números en FLIN tienen métodos estilo propiedad que se leen como inglés:

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

Estas propiedades eliminan una cantidad sorprendente de sentencias if. En lugar de if (n > 0), escribes if n.is_positive. En lugar de if (n % 2 == 0), escribes if n.is_even. La intención es más clara, y el código se lee como una especificación en lugar de una implementación.

Formato de números

flinn = 1234567.89
n.format()                 // "1,234,567.89" (según la localización)
n.format(2)                // "1234567.89" (2 decimales)
n.to_fixed(2)              // "1234567.89" (siempre 2 decimales)
n.to_percent               // "123456789%" (multiplicar por 100, agregar %)
n.to_hex                   // "12d687" (hexadecimal)
n.to_binary                // "100101011010000110000111" (binario)

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

format() es sensible a la localización. En la localización francesa, 1234.56.format() produce "1 234,56" (espacio como separador de miles, coma como separador decimal). En la localización inglesa, produce "1,234.56". La localización se determina por la configuración de la aplicación, no por la localización del sistema del servidor. Esto importa para el público objetivo de FLIN -- desarrolladores africanos construyendo aplicaciones para usuarios que hablan francés, inglés, árabe, portugués y docenas de idiomas locales.

Estadísticas: más allá del promedio

Cuando Thales describió los paneles de análisis que quería para Deblo.ai -- mostrando distribuciones de rendimiento estudiantil, identificando valores atípicos, calculando intervalos de confianza -- quedó claro que las funciones básicas sum y average no eran suficientes. FLIN necesitaba estadísticas reales.

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

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

// Estas son las funciones que importan para analítica real:
scores.median              // 87.0
scores.mode                // none (sin valores repetidos)
scores.std_dev             // 7.46 (desviación estándar)
scores.variance            // 55.6
scores.percentile(90)      // 94.1 (percentil 90)
scores.range               // 23 (max - min)

median ordena la lista y devuelve el valor central (o el promedio de los dos valores centrales para listas de longitud par). std_dev calcula la desviación estándar poblacional. variance es el cuadrado de la desviación estándar. percentile(p) usa interpolación lineal entre puntos de datos, igualando el comportamiento de la función PERCENTILE.INC de Excel.

Estas funciones operan sobre listas de números. Si la lista contiene valores none, se omiten silenciosamente. Si la lista está vacía, devuelven none. Este manejo elegante de datos faltantes es crítico para la analítica del mundo real donde los datos siempre están incompletos.

flin// Datos del mundo real con huecos
temperatures = [22.1, none, 23.4, 21.8, none, 24.2, 22.9]
temperatures.average       // 22.88 (valores none omitidos)
temperatures.count         // 7 (elementos totales)
temperatures.count(t => t != none)  // 5 (elementos no-none)

Correlación y regresión

Para analítica más avanzada, FLIN incluye correlación y regresión lineal simple:

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

correlation(hours_studied, exam_scores)  // 0.99 (positiva fuerte)

// Regresión lineal simple
model = linear_regression(hours_studied, exam_scores)
model.slope                // 3.57
model.intercept            // 58.57
model.r_squared            // 0.98

// Predecir
predicted = model.predict(6)  // 80.0

¿Es la regresión lineal una función "básica"? Para un lenguaje dirigido a plataformas educativas -- donde los profesores quieren mostrar la relación entre tiempo de estudio y notas -- absolutamente lo es. Para un lenguaje dirigido a aplicaciones financieras -- donde los analistas grafican ingresos contra gasto en marketing -- absolutamente lo es. La función son 40 líneas de Rust. Agrega un tamaño insignificante al binario. Y ahorra a cada desarrollador de escribir la suya (o encontrar, evaluar e instalar una biblioteca de estadísticas).

Trigonometría

Las funciones trigonométricas existen porque los gráficos, las animaciones y los cálculos de mapas las necesitan:

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

// Convertir entre grados y radianes
to_radians(180)            // PI
to_degrees(PI)             // 180.0

Las constantes PI y E están disponibles sin ninguna importación:

flinPI                         // 3.14159265358979
E                          // 2.71828182845904
INFINITY                   // Infinito positivo
NEG_INFINITY               // Infinito negativo

Estas no son variables. Son constantes verdaderas, resueltas en tiempo de compilación. No puedes reasignar PI. El compilador rechaza PI = 3 con un mensaje de error claro.

Geometría: la necesidad sorprendente

Las funciones de geometría fueron la adición más debatida. "¿Realmente necesita un lenguaje de programación web distance y area_circle?" La respuesta vino de tres casos de uso que aparecían en cada aplicación moderna que analizamos.

Mapas y ubicación. Toda aplicación con un mapa -- transporte compartido, entregas, localizadores de tiendas -- necesita cálculos de distancia entre coordenadas:

flin// Distancia haversine entre dos coordenadas 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

Renderizado de gráficos. Los gráficos de pastel necesitan ángulos. Los gráficos de radar necesitan coordenadas polares. Los gráficos de dispersión necesitan distancias entre puntos:

flin// Convertir polar a cartesiano para renderizado de gráficos
angle = 45
radius = 100
point = polar_to_cartesian(angle, radius)
// { x: 70.71, y: 70.71 }

// Rotar un punto alrededor de un centro
rotated = rotate_point(
    { x: 100, y: 0 },    // punto
    { x: 0, y: 0 },       // centro
    90                      // grados
)
// { x: 0, y: 100 }

Cálculos de UI. Posicionamiento de tooltips, detección de colisiones para arrastrar y soltar, cálculos de diseño responsivo:

flin// Verificar si un punto está dentro de un rectángulo
inside = point_in_rect(
    { x: 50, y: 50 },           // punto
    { x: 0, y: 0, w: 100, h: 100 }  // rectángulo
)
// true

// Distancia entre dos puntos
d = distance(
    { x: 0, y: 0 },
    { x: 3, y: 4 }
)
// 5.0 (teorema de Pitágoras)

Funciones de área y perímetro

flinarea_circle(10)            // 314.159... (radio 10)
area_rectangle(5, 10)      // 50
area_triangle(3, 4, 5)     // 6.0 (fórmula de Herón)

perimeter_circle(10)       // 62.832... (circunferencia)
perimeter_rectangle(5, 10) // 30

Son funciones simples -- la mayoría son de una línea en Rust. Pero tenerlas integradas significa que un desarrollador construyendo una visualización de geometría para una aplicación de educación matemática (como Deblo.ai) no necesita derivar la fórmula de Herón desde cero o buscar una biblioteca de geometría.

Detalles de implementación

Las funciones matemáticas se implementan en dos capas. Las funciones básicas (abs, min, max, floor, ceil, round) son opcodes dedicados de la VM para máximo rendimiento. Las funciones avanzadas (std_dev, percentile, haversine_distance) se implementan como funciones nativas registradas en la tabla de funciones de la VM.

rust// Matemáticas básicas: opcodes dedicados (ruta más rápida)
Op::Abs => {
    let val = self.pop_number()?;
    self.push(Value::Float(val.abs()));
}

// Matemáticas avanzadas: función nativa (aún rápida, ligeramente más overhead)
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()))
}

La ruta de función nativa tiene ligeramente más overhead que un opcode dedicado (una búsqueda en tabla hash para encontrar la función, más la llamada a la función en sí), pero para funciones que operan sobre listas enteras, el overhead es insignificante comparado con el cálculo en sí. Calcular la desviación estándar de 1,000 números toma microsegundos. La búsqueda de función toma nanosegundos.

Constantes y precisión

FLIN usa números de punto flotante de 64 bits (IEEE 754 f64) para todas las operaciones numéricas. Esto proporciona 15-17 dígitos decimales significativos de precisión -- suficiente para todo caso de uso de aplicación web.

Para cálculos financieros donde la aritmética decimal exacta importa, FLIN proporciona funciones de redondeo que producen resultados exactos:

flin// Aritmética de divisas
price = 19.99
tax_rate = 0.075
tax = round(price * tax_rate, 2)    // 1.50 (exacto)
total = round(price + tax, 2)       // 21.49 (exacto)

La función round(value, places) usa la estrategia "redondear mitad al par" (también llamada redondeo bancario) para evitar el sesgo sistemático que introduce "redondear mitad hacia arriba". Esta es la misma estrategia de redondeo usada por el round() de Python, IEEE 754 y los estándares de software financiero. round(2.5, 0) es 2, no 3. round(3.5, 0) es 4. A lo largo de miles de transacciones, esto elimina el sesgo ascendente que crea "redondear mitad hacia arriba".

Lo que deliberadamente dejamos fuera

Diseñar una biblioteca estándar es tanto sobre lo que excluyes como sobre lo que incluyes. Deliberadamente dejamos fuera:

Operaciones matriciales. La multiplicación, inversión y descomposición de matrices son esenciales para el aprendizaje automático y gráficos 3D. No son esenciales para aplicaciones web. Un desarrollador que necesita matrices está mejor servido por una biblioteca dedicada de álgebra lineal que por funciones integradas.

Matemáticas simbólicas. La resolución de ecuaciones, diferenciación e integración son funciones de matemáticas académicas. No pertenecen a la biblioteca estándar de un lenguaje de programación web.

Aritmética de precisión arbitraria. Los tipos BigInteger y BigDecimal agregan complejidad significativa al runtime para casos de uso que el 99% de los desarrolladores web nunca encuentran. Si FLIN alguna vez los necesita, se agregarán como un módulo separado, no como funciones integradas.

La línea que trazamos: si una función aparece en más del 5% de las aplicaciones web, pertenece a la biblioteca estándar. Si aparece en menos del 1%, no. Las funciones entre el 1% y el 5% se evaluaron caso por caso.

El resultado: matemáticas sin dependencias

Después de las Sesiones 122-128, la biblioteca matemática de FLIN cubría:

  • 28 funciones numéricas básicas (aritmética, redondeo, aleatorio)
  • 7 propiedades de números (is_positive, is_even, sign, etc.)
  • 6 funciones de formato (format, to_hex, to_percent, etc.)
  • 8 funciones de trigonometría (sin, cos, tan, atan2, etc.)
  • 12 funciones de estadísticas (median, std_dev, percentile, etc.)
  • 24 funciones de geometría (distance, area, haversine, etc.)
  • 4 constantes matemáticas (PI, E, INFINITY, NEG_INFINITY)

Un total de 89 funciones, todas disponibles sin una sola sentencia de importación. Suficientes para construir paneles de control, renderizar gráficos, validar cálculos financieros, calcular distancias y analizar distribuciones de datos -- todo en un lenguaje diseñado para desarrolladores web que quieren escribir lógica de aplicación, no gestionar dependencias.


Esta es la Parte 73 de la serie "Cómo construimos FLIN", que documenta cómo un CEO en Abiyán y un CTO de IA construyeron una biblioteca matemática directamente en el runtime de un lenguaje de programación.

Navegación de la serie: - [72] 31 métodos de cadena integrados en el lenguaje - [73] Funciones matemáticas, estadísticas y de geometría (estás aquí) - [74] Funciones de tiempo y zona horaria - [75] Cliente HTTP integrado en el lenguaje

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles