Math
Using it
std.math holds the integer helpers and the mathematical constants. A function of a float is not in the module: it is a method of f64, written on the value as Rust writes it, so a square root is x.sqrt(), a power is x.powi(n) or x.powf(y), and a limit such as the largest f64 is a static on the type, f64.max(). A program imports what it uses from the module by name:
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
}Interpolation writes a float with six significant digits, which is why PI prints as 3.14159; std.fmt writes more when the text matters (see Strings).
Integers
Every helper takes and answers i64, except the two predicates, which answer a bool.
| Function | What it answers |
|---|---|
abs(x: i64): i64 | the magnitude |
sign(x: i64): i64 | -1, 0 or 1 |
min(a: i64, b: i64): i64 | the smaller |
max(a: i64, b: i64): i64 | the larger |
clamp(x: i64, lo: i64, hi: i64): i64 | lo below lo, hi above hi, x between them |
pow(base: i64, exp: i64): i64 | base multiplied by itself exp times; pow(0, 0) is 1, and a negative exp answers 0 |
gcd(a: i64, b: i64): i64 | the greatest common divisor, never negative: gcd(-4, 6) is 2, gcd(0, 5) is 5 and gcd(0, 0) is 0 |
lcm(a: i64, b: i64): i64 | the least common multiple, never negative, and 0 when either side is 0 |
is_even(x: i64): bool | whether x is even, negatives included: is_even(-4) is true |
is_odd(x: i64): bool | whether x is odd: is_odd(-3) is true |
An answer past i64 overflows as integer arithmetic does (Types): a panic with integer overflow, and a wrap in a build with --release. The smallest i64, -9223372036854775808, has no magnitude that fits, so abs and gcd of it panic; so do pow(2, 63) and lcm(9223372036854775807, 2). Under --release, abs of the smallest i64 answers it unchanged.
Constants
| Constant | Value | What it is |
|---|---|---|
PI | 3.1415926535897931 | the ratio of a circle's circumference to its diameter |
TAU | 6.2831853071795862 | the full turn, 2 * PI |
E | 2.7182818284590451 | Euler's number, the base of ln |
SQRT_2 | 1.4142135623730951 | the square root of 2 |
FRAC_1_SQRT_2 | 0.70710678118654757 | 1 / SQRT_2 |
LN_2 | 0.69314718055994529 | the natural logarithm of 2 |
LN_10 | 2.3025850929940459 | the natural logarithm of 10 |
LOG2_E | 1.4426950408889634 | the base-2 logarithm of E |
LOG10_E | 0.43429448190325182 | the base-10 logarithm of E |
FRAC_PI_2 | 1.5707963267948966 | PI / 2, a right angle |
FRAC_PI_4 | 0.78539816339744828 | PI / 4 |
Each is the f64 nearest the real number, written to 17 significant digits, which is enough to name that double and no other. fmt.shortest(PI) writes the shortest text that reads back as the same double, 3.141592653589793.
The methods of f64
Every method answers an f64 except the five predicates, which answer a bool, and every argument is an f64 except powi's, which is an i64. The names are Rust's, so the natural logarithm is ln and log takes its base. x is the receiver.
| Group | Methods |
|---|---|
| Roots and powers | x.sqrt(), x.cbrt(), x.powf(y) for a real exponent, x.powi(n) for an i64 exponent, negative allowed, x.hypot(y) |
| Exponentials and logarithms | x.exp(), x.exp2(), x.exp_m1(), x.ln() for the natural logarithm, x.log2(), x.log10(), x.ln_1p(), x.log(base) |
| Rounding | x.floor(), x.ceil(), x.trunc() toward zero, x.round() with a half rounding away from zero, x.fract() |
| Trigonometry, in radians | x.sin(), x.cos(), x.tan(), x.asin(), x.acos(), x.atan(), y.atan2(x) with the receiver as y, x.to_degrees(), x.to_radians() |
| Hyperbolic | x.sinh(), x.cosh(), x.tanh(), x.asinh(), x.acosh(), x.atanh() |
| Sign and arithmetic | x.abs(), x.signum(), x.copysign(s), x.recip(), x.mul_add(a, b) for x * a + b with one rounding |
| Comparison | x.min(y), x.max(y), x.clamp(lo, hi) |
| Classification | x.is_nan(), x.is_infinite(), x.is_finite(), x.is_sign_negative(), x.is_sign_positive() |
| Limits, on the type | f64.max(), f64.min_positive(), f64.epsilon(), f64.infinity(), f64.neg_infinity(), f64.nan() |
f64.max() is the largest finite value, 1.7976931348623157e+308; f64.min_positive() is the smallest normal one, 2.2250738585072014e-308; and f64.epsilon() is the gap between 1.0 and the next double, 2.220446049250313e-16. The limits are calls on the type because a constant holds a literal, and no literal spells an infinity or a NaN.
What a float answers at its edges
No method panics. A method answers what IEEE 754 and the platform's C math library answer: (-1.0).sqrt() and 2.0.asin() are NaN, 0.0.ln() is -inf, and 0.0.powi(-1) and 1.0.atanh() are inf. Division answers too: 1.0 / 0.0 is inf, -1.0 / 0.0 is -inf and 0.0 / 0.0 is NaN, where an integer division by zero panics.
A NaN equals nothing, itself included. x == x is false for a NaN and x != x is true, and <, >, <= and >= are all false when either side is NaN, so a program asks x.is_nan(). x.min(y) and x.max(y) answer the number when the other side is NaN, so f64.nan().max(1.0) is 1.0. x.clamp(lo, hi) answers NaN for a NaN x, takes a NaN bound as no bound, and answers hi when lo is above hi.
round takes a half away from zero: 2.5.round() is 3.0, (-2.5).round() is -3.0 and 0.5.round() is 1.0. trunc goes toward zero, floor down and ceil up, and fract keeps the sign of x: (-2.75).fract() is -0.75.
Zero has a sign. -0.0 == 0.0 is true, and the two differ in the sign bit: (-0.0).is_sign_negative() is true, (-0.0).signum() is -1.0, and (-0.0).abs() is 0.0.
Compare within a tolerance. A float is the double nearest the real number, so arithmetic on two of them rounds: 0.1 + 0.2 == 0.3 is false. A test compares a computed float with std.testing.assert_near_f64, which takes the value, the expected value, the tolerance and a description, and fails on a NaN. An exact comparison is right where the answer is exact, as it is for the rounding methods and for sqrt of a perfect square: 9.0.sqrt() == 3.0 is true.
A method on a negative literal goes in parentheses. 2.0.sqrt() is the square root of 2.0, but -2.5.floor() negates 2.5.floor() and is -2.0, where (-2.5).floor() is -3.0.
Samples
A circle and a triangle
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 13Rounding a price
as i64 drops the fraction, so a price that has no exact binary form loses a cent unless it is rounded first.
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 centsAngles
The trigonometric methods take radians. y.atan2(x) is the angle of the point (x, y), so its receiver is 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 degreesReducing a fraction
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/12Testing for 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