Ir para o conteúdo

Matemática ​

Como usar ​

std.math guarda os helpers de inteiros e as constantes matemáticas. Uma função de um float não fica no module: ela é um método de f64, escrito no valor como o Rust escreve, então uma raiz quadrada é x.sqrt(), uma potência é x.powi(n) ou x.powf(y), e um limite como o maior f64 é um static do tipo, f64.max(). Um programa importa do module, pelo nome, o que usa:

talor
use std.math.{PI, gcd};

fn main() {
    println(`${gcd(12, 18)}`);      // 6: a function of the module
    println(`${PI}`);               // 3.14159: a constant of the module
    println(`${2.0.sqrt()}`);       // 1.41421: a method of f64
}

A interpolação escreve um float com seis dígitos significativos, e é por isso que PI sai como 3.14159; std.fmt escreve mais quando o texto importa (veja Strings).

Inteiros ​

Todo helper recebe e responde i64, exceto os dois de paridade, que respondem um bool.

FunçãoO que responde
abs(x: i64): i64a magnitude
sign(x: i64): i64-1, 0 ou 1
min(a: i64, b: i64): i64o menor
max(a: i64, b: i64): i64o maior
clamp(x: i64, lo: i64, hi: i64): i64lo abaixo de lo, hi acima de hi, x entre os dois
pow(base: i64, exp: i64): i64base multiplicada por ela mesma exp vezes; pow(0, 0) é 1, e um exp negativo responde 0
gcd(a: i64, b: i64): i64o máximo divisor comum, nunca negativo: gcd(-4, 6) é 2, gcd(0, 5) é 5 e gcd(0, 0) é 0
lcm(a: i64, b: i64): i64o mínimo múltiplo comum, nunca negativo, e 0 quando um dos lados é 0
is_even(x: i64): boolse x é par, negativos inclusive: is_even(-4) é true
is_odd(x: i64): boolse x é ímpar: is_odd(-3) é true

Uma resposta que passa de i64 faz overflow como a aritmética inteira faz (Tipos): um panic com integer overflow, e um wrap num build com --release. O menor i64, -9223372036854775808, não tem magnitude que caiba, então abs e gcd dele dão panic; o mesmo vale para pow(2, 63) e lcm(9223372036854775807, 2). Com --release, abs do menor i64 responde o próprio valor, sem mudança.

Constantes ​

ConstanteValorO que é
PI3.1415926535897931a razão entre a circunferência de um círculo e o seu diâmetro
TAU6.2831853071795862a volta completa, 2 * PI
E2.7182818284590451o número de Euler, a base de ln
SQRT_21.4142135623730951a raiz quadrada de 2
FRAC_1_SQRT_20.707106781186547571 / SQRT_2
LN_20.69314718055994529o logaritmo natural de 2
LN_102.3025850929940459o logaritmo natural de 10
LOG2_E1.4426950408889634o logaritmo de E na base 2
LOG10_E0.43429448190325182o logaritmo de E na base 10
FRAC_PI_21.5707963267948966PI / 2, um ângulo reto
FRAC_PI_40.78539816339744828PI / 4

Cada uma é o f64 mais próximo do número real, escrito com 17 dígitos significativos, o bastante para nomear esse double e nenhum outro. fmt.shortest(PI) escreve o texto mais curto que é lido de volta como o mesmo double, 3.141592653589793.

Os métodos de f64 ​

Todo método responde um f64, exceto os cinco de classificação, que respondem um bool, e todo argumento é um f64, exceto o de powi, que é um i64. Os nomes são os do Rust, então o logaritmo natural é ln e log recebe a sua base. x é o receiver.

GrupoMétodos
Raízes e potênciasx.sqrt(), x.cbrt(), x.powf(y) para um expoente real, x.powi(n) para um expoente i64, negativo inclusive, x.hypot(y)
Exponenciais e logaritmosx.exp(), x.exp2(), x.exp_m1(), x.ln() para o logaritmo natural, x.log2(), x.log10(), x.ln_1p(), x.log(base)
Arredondamentox.floor(), x.ceil(), x.trunc() em direção a zero, x.round() com a metade arredondada para longe de zero, x.fract()
Trigonometria, em radianosx.sin(), x.cos(), x.tan(), x.asin(), x.acos(), x.atan(), y.atan2(x) com o receiver como y, x.to_degrees(), x.to_radians()
Hiperbólicasx.sinh(), x.cosh(), x.tanh(), x.asinh(), x.acosh(), x.atanh()
Sinal e aritméticax.abs(), x.signum(), x.copysign(s), x.recip(), x.mul_add(a, b) para x * a + b com um único arredondamento
Comparaçãox.min(y), x.max(y), x.clamp(lo, hi)
Classificaçãox.is_nan(), x.is_infinite(), x.is_finite(), x.is_sign_negative(), x.is_sign_positive()
Limites, no tipof64.max(), f64.min_positive(), f64.epsilon(), f64.infinity(), f64.neg_infinity(), f64.nan()

f64.max() é o maior valor finito, 1.7976931348623157e+308; f64.min_positive() é o menor valor normal, 2.2250738585072014e-308; e f64.epsilon() é a distância entre 1.0 e o double seguinte, 2.220446049250313e-16. Os limites são chamadas no tipo porque uma constante guarda um literal, e nenhum literal escreve um infinito ou um NaN.

O que um float responde nos seus extremos ​

Nenhum método dá panic. Um método responde o que o IEEE 754 e a biblioteca matemática C da plataforma respondem: (-1.0).sqrt() e 2.0.asin() são NaN, 0.0.ln() é -inf, e 0.0.powi(-1) e 1.0.atanh() são inf. A divisão também responde: 1.0 / 0.0 é inf, -1.0 / 0.0 é -inf e 0.0 / 0.0 é NaN, onde a divisão de um inteiro por zero dá panic.

Um NaN não é igual a nada, nem a si mesmo. x == x é falso para um NaN e x != x é verdadeiro, e <, >, <= e >= são todos falsos quando um dos lados é NaN, então um programa pergunta x.is_nan(). x.min(y) e x.max(y) respondem o número quando o outro lado é NaN, então f64.nan().max(1.0) é 1.0. x.clamp(lo, hi) responde NaN para um x NaN, trata um bound NaN como nenhum bound, e responde hi quando lo está acima de hi.

round leva a metade para longe de zero: 2.5.round() é 3.0, (-2.5).round() é -3.0 e 0.5.round() é 1.0. trunc vai em direção a zero, floor para baixo e ceil para cima, e fract mantém o sinal de x: (-2.75).fract() é -0.75.

O zero tem sinal. -0.0 == 0.0 é verdadeiro, e os dois diferem no bit de sinal: (-0.0).is_sign_negative() é verdadeiro, (-0.0).signum() é -1.0, e (-0.0).abs() é 0.0.

Compare dentro de uma tolerância. Um float é o double mais próximo do número real, então a aritmética com dois deles arredonda: 0.1 + 0.2 == 0.3 é falso. Um teste compara um float calculado com std.testing.assert_near_f64, que recebe o valor, o valor esperado, a tolerância e uma descrição, e falha diante de um NaN. Uma comparação exata está certa onde a resposta é exata, como nos métodos de arredondamento e no sqrt de um quadrado perfeito: 9.0.sqrt() == 3.0 é verdadeiro.

Um método num literal negativo vai entre parênteses. 2.0.sqrt() é a raiz quadrada de 2.0, mas -2.5.floor() nega 2.5.floor() e é -2.0, enquanto (-2.5).floor() é -3.0.

Exemplos ​

Um círculo e um triângulo ​

talor
use std.math.{PI, TAU};

fn circle_area(r: f64): f64 {
    PI * r.powi(2)
}

fn main() {
    let r = 3.0;
    println(`radius ${r}: area ${circle_area(r)}, circumference ${TAU * r}`);
    let a = 5.0;
    let b = 12.0;
    println(`legs ${a} and ${b}: hypotenuse ${a.hypot(b)}`);
}
radius 3: area 28.2743, circumference 18.8496
legs 5 and 12: hypotenuse 13

Arredondando um preço ​

as i64 descarta a fração, então um preço que não tem forma binária exata perde um centavo se não for arredondado antes.

talor
fn main() {
    let price = 1.15;
    let scaled = price * 100.0;        // 114.99999999999999: 1.15 has no exact binary form
    println(`${scaled as i64} cents truncated, ${scaled.round() as i64} rounded`);
    let total = 7.0 * price;
    println(`${total.floor()} whole and ${(total.fract() * 100.0).round()} cents`);
}
114 cents truncated, 115 rounded
8 whole and 5 cents

Ângulos ​

Os métodos trigonométricos recebem radianos. y.atan2(x) é o ângulo do ponto (x, y), então o seu receiver é y.

talor
fn main() {
    let angle = 30.0;
    println(`sin of ${angle} degrees: ${angle.to_radians().sin()}`);
    // The direction of the point (-1, 1) seen from the origin.
    let x = -1.0;
    let y = 1.0;
    println(`the point (${x}, ${y}) lies at ${y.atan2(x).to_degrees()} degrees`);
}
sin of 30 degrees: 0.5
the point (-1, 1) lies at 135 degrees

Reduzindo uma fração ​

talor
use std.math.{gcd, lcm};

fn main() {
    let num = 42;
    let den = 56;
    let g = gcd(num, den);
    println(`${num}/${den} reduces to ${num / g}/${den / g}`);
    // 1/4 + 1/6 over the smallest common denominator.
    let d = lcm(4, 6);
    println(`1/4 + 1/6 = ${d / 4 + d / 6}/${d}`);
}
42/56 reduces to 3/4
1/4 + 1/6 = 5/12

Testando NaN ​

talor
fn describe(x: f64): string {
    if x.is_nan() {
        "not a number"
    } else if x.is_infinite() {
        "infinite"
    } else {
        `${x}`
    }
}

fn main() {
    let zero = 0.0;
    println(describe(1.0 / 4.0));
    println(describe(1.0 / zero));
    println(describe(zero / zero));
    println(describe((-1.0).sqrt()));
    let nan = f64.nan();
    println(`${nan == nan} ${nan != nan} ${nan.max(1.0)}`);
}
0.25
infinite
not a number
not a number
false true 1

Talor v0.1.0 - Publicado sob a licença MIT OR Apache-2.0.