Capítulo 15 12 min de leitura

Math

Auxiliares numéricos para Glide. As funções de ponto flutuante fazem binding diretamente para a biblioteca matemática C (libm); os auxiliares inteiros e os min/max/clamp de float são Glide puro e portanto monomorhizam de forma limpa. Esta página também exporta as constantes PI e E.

Import

import stdlib::math::*;

A vinculação com libm é automática — não há nenhuma flag a passar.

Visão geral da superfície

Grupo Itens
Constantes PI, E
Raízes / potências / exp sqrt, pow, exp
Arredondamento floor, ceil, round, fabs
Trigonometria sin, cos, tan, atan2
Logaritmos log, log2, log10
Auxiliares inteiros min_int, max_int, abs_int, sign_int, clamp_int, ipow, gcd, lcm
Auxiliares float min_f64, max_f64, clamp_f64

As 14 funções de ponto flutuante são bindings finos de extern fn para libm; todo o resto é Glide puro. Nenhuma das funções matemáticas retorna !T/?T — não há nada a desempacotar. As funções float sinalizam entradas fora do domínio com NaN/inf do IEEE-754 (veja Semântica IEEE), e os auxiliares inteiros envolvem ou transbordam silenciosamente em alvos de complemento a dois, em vez de retornar erro.

Constantes

Nome Tipo Valor
PI f64 3.141592653589793
E f64 2.718281828459045
pub const PI: f64 = 3.141592653589793;
pub const E:  f64 = 2.718281828459045;
import stdlib::math::*;

fn main() -> i32 {
    let area: f64 = PI * 2.0 * 2.0;   // PI * r^2 com r = 2
    let growth: f64 = E * E;          // e^2
    println!("area", area);
    println!("growth", growth);
    return 0;
}

Funções de ponto flutuante

São bindings de extern fn para libm. Todas recebem e retornam f64. Não retornam !T/?T — entradas fora do domínio seguem a semântica C/IEEE-754 (por exemplo, sqrt de um valor negativo retorna NaN; log(0.0) retorna -inf), portanto não há valor de erro a desempacotar. Veja IEEE-754 NaN e inf para entender como esses valores se propagam.

Raízes, potências e exponenciais

Função Assinatura Descrição
sqrt pub extern fn sqrt(x: f64) -> f64 Raiz quadrada. Retorna NaN para entrada negativa.
pow pub extern fn pow(x: f64, y: f64) -> f64 x^y como float. Para expoentes inteiros, prefira ipow.
exp pub extern fn exp(x: f64) -> f64 e^x.
import stdlib::math::*;

fn main() -> i32 {
    let r: f64 = sqrt(9.0);       // 3.0
    let p: f64 = pow(2.0, 10.0);  // 1024.0
    let e2: f64 = exp(1.0);       // E (≈ 2.71828)
    println!("sqrt", r);
    println!("pow", p);
    println!("exp", e2);
    return 0;
}

Arredondamento

Função Assinatura Descrição
floor pub extern fn floor(x: f64) -> f64 Arredonda em direção a -∞. floor(-2.3) == -3.0.
ceil pub extern fn ceil(x: f64) -> f64 Arredonda em direção a +∞. ceil(-2.7) == -2.0.
round pub extern fn round(x: f64) -> f64 Arredonda para o mais próximo; empates se afastam do zero. round(2.5) == 3.0.
fabs pub extern fn fabs(x: f64) -> f64 Valor absoluto (float). Use abs_int para inteiros.
import stdlib::math::*;

fn main() -> i32 {
    let f: f64 = floor(2.7);   //  2.0
    let c: f64 = ceil(2.1);    //  3.0
    let rn: f64 = round(2.5);  //  3.0
    let a: f64 = fabs(-1.5);   //  1.5
    println!("floor", f);
    println!("ceil", c);
    println!("round", rn);
    println!("fabs", a);
    return 0;
}

Trigonometria

Os ângulos estão em radianos. Converta de graus com graus * PI / 180.0.

Função Assinatura Descrição
sin pub extern fn sin(x: f64) -> f64 Seno. Argumento em radianos.
cos pub extern fn cos(x: f64) -> f64 Cosseno. Argumento em radianos.
tan pub extern fn tan(x: f64) -> f64 Tangente. Argumento em radianos.
atan2 pub extern fn atan2(y: f64, x: f64) -> f64 Arco-tangente de dois argumentos: ângulo completo do ponto (x, y) em radianos.
import stdlib::math::*;

fn main() -> i32 {
    let s: f64 = sin(PI / 2.0);     // 1.0
    let co: f64 = cos(PI);          // -1.0
    let t: f64 = tan(PI / 4.0);     // 1.0
    let ang: f64 = atan2(1.0, 0.0); // PI / 2  (point straight up)
    println!("sin", s);
    println!("cos", co);
    println!("tan", t);
    println!("atan2", ang);
    return 0;
}

Logaritmos

Função Assinatura Descrição
log pub extern fn log(x: f64) -> f64 Logaritmo natural (base e). Domínio x > 0.
log2 pub extern fn log2(x: f64) -> f64 Logaritmo base 2.
log10 pub extern fn log10(x: f64) -> f64 Logaritmo base 10.
import stdlib::math::*;

fn main() -> i32 {
    let l: f64 = log(E);          // 1.0
    let l2: f64 = log2(8.0);      // 3.0
    let l10: f64 = log10(1000.0); // 3.0
    println!("log", l);
    println!("log2", l2);
    println!("log10", l10);
    return 0;
}

IEEE-754 NaN e inf

Como as funções float fazem binding diretamente para libm, entradas fora do domínio produzem valores especiais do IEEE-754 em vez de levantar um erro. As consequências mais importantes:

  • sqrt(-1.0), log(-1.0), pow(-1.0, 0.5)NaN.
  • log(0.0)-inf; pow(0.0, -1.0)+inf.
  • Um NaN é não-ordenado: toda comparação contra ele (==, <, >) resulta em false, incluindo nan == nan. Detecte-o com o truque da auto-desigualdade x != x.
  • NaN se propaga pela aritmética e pelas funções min_f64/max_f64/clamp_f64 (que usam comparações simples com </>, de modo que um operando NaN pode passar inalterado).
import stdlib::math::*;

fn main() -> i32 {
    let nan: f64 = sqrt(-1.0);       // NaN
    let neginf: f64 = log(0.0);      // -inf
    let is_nan: bool = nan != nan;   // true  — o teste canônico para NaN
    println!("nan", nan);
    println!("neginf", neginf);
    println!("is_nan", is_nan);
    println!("min-with-nan", min_f64(nan, 1.0));  // NaN propagates
    return 0;
}

Auxiliares inteiros

Funções Glide puro sobre i32. Nunca alocam e nunca falham; os resultados são i32 simples (sem !T).

Função Assinatura Descrição
min_int pub fn min_int(a: i32, b: i32) -> i32 O menor de dois inteiros.
max_int pub fn max_int(a: i32, b: i32) -> i32 O maior de dois inteiros.
abs_int pub fn abs_int(a: i32) -> i32 Valor absoluto. abs_int(INT_MIN) transborda.
sign_int pub fn sign_int(a: i32) -> i32 Sinal: -1, 0 ou +1.
clamp_int pub fn clamp_int(x: i32, lo: i32, hi: i32) -> i32 Restringe ao intervalo inclusivo [lo, hi].
ipow pub fn ipow(a: i32, b: i32) -> i32 a^b para b não-negativo, por quadratura repetida (O(log b)). Envolve ao transbordar.
gcd pub fn gcd(a: i32, b: i32) -> i32 Máximo divisor comum (Euclideano). Sempre não-negativo.
lcm pub fn lcm(a: i32, b: i32) -> i32 Mínimo múltiplo comum. Retorna 0 se qualquer argumento for 0.
import stdlib::math::*;

fn main() -> i32 {
    let mn: i32 = min_int(7, 3);        // 3
    let mx: i32 = max_int(7, 3);        // 7
    let av: i32 = abs_int(-5);          // 5
    let sg: i32 = sign_int(-9);         // -1
    let cl: i32 = clamp_int(15, 0, 10); // 10
    let ip: i32 = ipow(2, 10);          // 1024
    let g: i32 = gcd(12, 18);           // 6
    let l: i32 = lcm(4, 6);             // 12
    println!("min_int", mn);
    println!("max_int", mx);
    println!("abs_int", av);
    println!("sign_int", sg);
    println!("clamp_int", cl);
    println!("ipow", ip);
    println!("gcd", g);
    println!("lcm", l);
    return 0;
}

gcd opera sobre valores absolutos, portanto gcd(-12, 8) == 4 e gcd(0, 0) == 0. lcm é calculado como abs_int(a / gcd(a, b) * b) para limitar o transbordamento no produto intermediário.

Auxiliares float

Min/max/clamp Glide puro sobre f64. O comportamento com NaN segue a semântica da libc — um operando NaN se propaga.

Função Assinatura Descrição
min_f64 pub fn min_f64(a: f64, b: f64) -> f64 O menor de dois f64.
max_f64 pub fn max_f64(a: f64, b: f64) -> f64 O maior de dois f64.
clamp_f64 pub fn clamp_f64(x: f64, lo: f64, hi: f64) -> f64 Restringe ao intervalo [lo, hi]. Veja as ressalvas de clamp_int.
import stdlib::math::*;

fn main() -> i32 {
    let mn: f64 = min_f64(1.5, 2.5);        // 1.5
    let mx: f64 = max_f64(1.5, 2.5);        // 2.5
    let cl: f64 = clamp_f64(2.0, 0.0, 1.0); // 1.0
    println!("min_f64", mn);
    println!("max_f64", mx);
    println!("clamp_f64", cl);
    return 0;
}

Exemplos práticos

Distância euclidiana

sqrt mais uma soma de quadrados fornece a distância em linha reta entre dois pontos — o bloco fundamental para código de colisão, vizinho mais próximo e física.

import stdlib::math::*;

fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
    let dx: f64 = x2 - x1;
    let dy: f64 = y2 - y1;
    return sqrt(dx * dx + dy * dy);
}

fn main() -> i32 {
    let d: f64 = distance(0.0, 0.0, 3.0, 4.0);  // 5.0
    println!("distance", d);
    return 0;
}

Graus e radianos

As funções trigonométricas recebem radianos. Encapsule o fator de conversão PI / 180.0 uma vez só para que os pontos de chamada fiquem legíveis.

import stdlib::math::*;

fn to_radians(deg: f64) -> f64 { return deg * PI / 180.0; }
fn to_degrees(rad: f64) -> f64 { return rad * 180.0 / PI; }

fn main() -> i32 {
    let r: f64 = to_radians(90.0);   // PI / 2
    println!("sin90", sin(r));       // 1.0
    println!("degrees", to_degrees(r)); // 90.0
    return 0;
}

Simplificando uma fração com gcd / lcm

Dividir numerador e denominador pelo gcd simplifica uma fração à forma irredutível. lcm encontra um denominador comum. Ambos operam sobre valores absolutos e nunca alocam.

import stdlib::math::*;

fn main() -> i32 {
    let num: i32 = 84;
    let den: i32 = 126;
    let g: i32 = gcd(num, den);   // 42
    println!("reduced", num / g, den / g);   // 2 3

    let l: i32 = lcm(lcm(4, 6), 8);          // 24
    println!("lcm", l);

    // gcd uses magnitudes; the zero edge cases:
    println!("gcd-neg", gcd(-12, 8));   // 4
    println!("gcd-zero", gcd(0, 0));    // 0
    println!("lcm-zero", lcm(0, 5));    // 0
    return 0;
}

Restringindo entradas, separando sinal e magnitude

clamp_int/clamp_f64 mantêm valores não-confiáveis dentro de um intervalo; sign_int + abs_int decompõem um valor com sinal em direção e magnitude.

import stdlib::math::*;

fn main() -> i32 {
    let vol: i32 = clamp_int(137, 0, 100);   // 100 — slider saturates
    let alpha: f64 = clamp_f64(1.4, 0.0, 1.0); // 1.0

    let v: i32 = -42;
    println!("dir", sign_int(v));   // -1
    println!("mag", abs_int(v));    // 42
    println!("vol", vol);
    println!("alpha", alpha);

    // clamp_int(x, lo, hi) is exactly max_int(lo, min_int(hi, x)):
    println!("manual", max_int(0, min_int(100, 137)));  // 100
    return 0;
}

Potências inteiras exatas — ipow vs pow

pow opera em ponto flutuante e pode se desviar de um inteiro exato; ipow permanece em i32 (com envolvimento ao transbordar). Use ipow sempre que ambos os operandos forem inteiros e o resultado couber em i32.

import stdlib::math::*;

fn main() -> i32 {
    let exact: i32 = ipow(10, 9);     // 1000000000, exact
    let approx: f64 = pow(10.0, 9.0); // may be 999999999.9999999...
    let recovered: i32 = floor(approx + 0.5) as i32;
    println!("ipow", exact);
    println!("pow-recovered", recovered);
    return 0;
}

Veja também

  • `stdlib::random` — geração de números aleatórios, frequentemente restringida com clamp_int/clamp_f64.
  • O capítulo de tipos numéricos do livro para literais i32/f64, casts (as) e semântica de transbordamento de inteiros.