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:
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ção | O que responde |
|---|---|
abs(x: i64): i64 | a magnitude |
sign(x: i64): i64 | -1, 0 ou 1 |
min(a: i64, b: i64): i64 | o menor |
max(a: i64, b: i64): i64 | o maior |
clamp(x: i64, lo: i64, hi: i64): i64 | lo abaixo de lo, hi acima de hi, x entre os dois |
pow(base: i64, exp: i64): i64 | base multiplicada por ela mesma exp vezes; pow(0, 0) é 1, e um exp negativo responde 0 |
gcd(a: i64, b: i64): i64 | o máximo divisor comum, nunca negativo: gcd(-4, 6) é 2, gcd(0, 5) é 5 e gcd(0, 0) é 0 |
lcm(a: i64, b: i64): i64 | o mínimo múltiplo comum, nunca negativo, e 0 quando um dos lados é 0 |
is_even(x: i64): bool | se x é par, negativos inclusive: is_even(-4) é true |
is_odd(x: i64): bool | se 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
| Constante | Valor | O que é |
|---|---|---|
PI | 3.1415926535897931 | a razão entre a circunferência de um círculo e o seu diâmetro |
TAU | 6.2831853071795862 | a volta completa, 2 * PI |
E | 2.7182818284590451 | o número de Euler, a base de ln |
SQRT_2 | 1.4142135623730951 | a raiz quadrada de 2 |
FRAC_1_SQRT_2 | 0.70710678118654757 | 1 / SQRT_2 |
LN_2 | 0.69314718055994529 | o logaritmo natural de 2 |
LN_10 | 2.3025850929940459 | o logaritmo natural de 10 |
LOG2_E | 1.4426950408889634 | o logaritmo de E na base 2 |
LOG10_E | 0.43429448190325182 | o logaritmo de E na base 10 |
FRAC_PI_2 | 1.5707963267948966 | PI / 2, um ângulo reto |
FRAC_PI_4 | 0.78539816339744828 | PI / 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.
| Grupo | Métodos |
|---|---|
| Raízes e potências | x.sqrt(), x.cbrt(), x.powf(y) para um expoente real, x.powi(n) para um expoente i64, negativo inclusive, x.hypot(y) |
| Exponenciais e logaritmos | x.exp(), x.exp2(), x.exp_m1(), x.ln() para o logaritmo natural, x.log2(), x.log10(), x.ln_1p(), x.log(base) |
| Arredondamento | x.floor(), x.ceil(), x.trunc() em direção a zero, x.round() com a metade arredondada para longe de zero, x.fract() |
| Trigonometria, em radianos | x.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ólicas | x.sinh(), x.cosh(), x.tanh(), x.asinh(), x.acosh(), x.atanh() |
| Sinal e aritmética | x.abs(), x.signum(), x.copysign(s), x.recip(), x.mul_add(a, b) para x * a + b com um único arredondamento |
| Comparação | x.min(y), x.max(y), x.clamp(lo, hi) |
| Classificação | x.is_nan(), x.is_infinite(), x.is_finite(), x.is_sign_negative(), x.is_sign_positive() |
| Limites, no tipo | f64.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
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 13Arredondando 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.
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.
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 degreesReduzindo uma fração
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/12Testando NaN
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