std.math
Reed's standard library. Imported with use std.math; not on disk.
Methods
| i32.abs | Absolute value of a signed 32-bit integer. |
| i64.abs | Absolute value of a signed 64-bit integer. |
| i32.min | The smaller of two signed integers. |
| i32.max | The larger of two signed integers. |
| i32.min_u | The smaller of two integers, compared as unsigned. |
| i32.max_u | The larger of two integers, compared as unsigned. |
| i64.min | The smaller of two signed 64-bit integers. |
| i64.max | The larger of two signed 64-bit integers. |
| i32.clamp | Constrains a value to the inclusive range lo..=hi. |
| i32.signum | The sign of a value: -1, 0, or 1. |
| i64.signum | The sign of a 64-bit value, as an i32. |
| i32.isqrt | Integer square root: the largest r with r * r <= self. |
| i32.pow | Raises self to a non-negative power, by binary exponentiation. |
| i64.pow | Raises a 64-bit self to a non-negative power, by binary exponentiation. |
| i32.gcd | Greatest common divisor, by the binary (Stein's) algorithm. |
| i32.lcm | Least common multiple. |
| i32.log2 | Floor of the base-2 logarithm: the index of the highest set bit. |
| i32.log10 | Floor of the base-10 logarithm: one less than the number of decimal digits. |
| i32.is_pow2 | Whether self is a power of two. |
| i32.next_pow2 | The smallest power of two greater than or equal to self. |
| i32.div_floor | Euclidean division: the quotient rounded toward negative infinity. |
| i32.mod_floor | Euclidean remainder: the remainder of div_floor, carrying the divisor's sign. |
| i32.sat_add | Addition that saturates at the i32 bounds instead of wrapping. |
| i32.sat_sub | Subtraction that saturates at the i32 bounds instead of wrapping. |
| i32.lerp | Linear interpolation between self and to, in fixed point. |
| i32.is_prime | Whether self is prime, by trial division against 2, 3, and then 6k±1. |
func i32.abs(self: i32) -> i32
Absolute value of a signed 32-bit integer.
Branchless: negating via the sign mask costs three instructions and no jump.
Overflow. (-2147483648).abs() returns -2147483648, because +2147483648 has no i32 representation. This mirrors what every two's-complement machine does and what wasm.i32.sub(0, x) would give; it is deliberately not a trap.
- Returns
- the magnitude of
self, except at i32 minimum
- See also
i64.abs
Source
func i32.abs(self: i32) -> i32 {
let mask: i32 = self >> 31;
return (self ^ mask) - mask;
}
func i64.abs(self: i64) -> i64
Absolute value of a signed 64-bit integer.
(-9223372036854775808).abs() returns itself, for the reason i32.abs documents.
- Returns
- the magnitude of
self, except at i64 minimum
Source
func i64.abs(self: i64) -> i64 {
let mask: i64 = self >> 63;
return (self ^ mask) - mask;
}
func i32.min(self: i32, rhs: i32) -> i32
The smaller of two signed integers.
WebAssembly's min instruction exists only for floats, so this is a real comparison and a select, not a single instruction.
- Parameters
- rhs — the value to compare against
- See also
i32.max, i32.min_u
Source
func i32.min(self: i32, rhs: i32) -> i32 {
return self < rhs ? self : rhs;
}
func i32.max(self: i32, rhs: i32) -> i32
The larger of two signed integers.
- Parameters
- rhs — the value to compare against
- See also
i32.min
Source
func i32.max(self: i32, rhs: i32) -> i32 {
return self > rhs ? self : rhs;
}
func i32.min_u(self: i32, rhs: i32) -> i32
The smaller of two integers, compared as unsigned.
Distinct from min, not a stylistic variant: (-1).min(1) is -1, while (-1).min_u(1) is 1, since -1 read as unsigned is 4294967295.
- Parameters
- rhs — the value to compare against
- See also
i32.min
Source
func i32.min_u(self: i32, rhs: i32) -> i32 {
return self.lt_u(rhs) ? self : rhs;
}
func i32.max_u(self: i32, rhs: i32) -> i32
The larger of two integers, compared as unsigned.
- Parameters
- rhs — the value to compare against
- See also
i32.max, i32.min_u
Source
func i32.max_u(self: i32, rhs: i32) -> i32 {
return self.gt_u(rhs) ? self : rhs;
}
func i64.min(self: i64, rhs: i64) -> i64
The smaller of two signed 64-bit integers.
- Parameters
- rhs — the value to compare against
Source
func i64.min(self: i64, rhs: i64) -> i64 {
return self < rhs ? self : rhs;
}
func i64.max(self: i64, rhs: i64) -> i64
The larger of two signed 64-bit integers.
- Parameters
- rhs — the value to compare against
Source
func i64.max(self: i64, rhs: i64) -> i64 {
return self > rhs ? self : rhs;
}
func i32.clamp(self: i32, lo: i32, hi: i32) -> i32
Constrains a value to the inclusive range lo..=hi.
When lo > hi the range is empty and there is no correct answer; this returns lo, matching the order the two comparisons are applied in, rather than trapping.
- Parameters
- lo — the lower bound, inclusive
- hi — the upper bound, inclusive
- See also
i32.min, i32.max
Source
func i32.clamp(self: i32, lo: i32, hi: i32) -> i32 {
if (self < lo) { return lo; }
if (self > hi) { return hi; }
return self;
}
func i32.signum(self: i32) -> i32
The sign of a value: -1, 0, or 1.
Branchless. Correct at i32 minimum, unlike a formulation built on abs.
- Returns
-1 when negative, 0 when zero, 1 when positive
Source
func i32.signum(self: i32) -> i32 {
return (self > 0 ? 1 : 0) - (self < 0 ? 1 : 0);
}
func i64.signum(self: i64) -> i32
The sign of a 64-bit value, as an i32.
The result is i32 rather than i64 because a sign is not a wide quantity and the caller almost always wants to compare or branch on it.
- Returns
-1 when negative, 0 when zero, 1 when positive
Source
func i64.signum(self: i64) -> i32 {
return (self > 0 ? 1 : 0) - (self < 0 ? 1 : 0);
}
func i32.isqrt(self: i32) -> i32
Integer square root: the largest r with r * r <= self.
Exact at every input, including every perfect square -- this is a digit-by-digit binary method over the integers, not a rounded f64.sqrt, which starts disagreeing with the exact answer for inputs above 2^52.
A negative input has no real square root; this returns 0 rather than trapping.
- Returns
- the floor of the square root, or
0 for a negative input
- See also
i32.pow
Source
func i32.isqrt(self: i32) -> i32 {
if (self <= 0) { return 0; }
var rem: i32 = self;
var root: i32 = 0;
var bit: i32 = 1 << ((31 - self.clz()) & ~1);
loop scan() {
if (bit == 0) { break scan(); }
if (rem >= root + bit) {
rem = rem - (root + bit);
root = (root >> 1) + bit;
} else {
root = root >> 1;
}
bit = bit >> 2;
continue scan();
}
return root;
}
func i32.pow(self: i32, exp: i32) -> i32
Raises self to a non-negative power, by binary exponentiation.
Wrapping. The result wraps modulo 2^32 exactly as * does; (2).pow(31) is -2147483648. There is no overflow check, so this stays a handful of instructions.
A negative exponent would give a fraction, which is not an i32; this returns 0 for one (matching the mathematical truncation of 1/x^n for |x| > 1), except that (1).pow(-n) and (-1).pow(-n) are not special-cased and also return 0.
- Parameters
- exp — the exponent; negative exponents yield
0
- Returns
self raised to exp, wrapping on overflow
Source
func i32.pow(self: i32, exp: i32) -> i32 {
if (exp < 0) { return 0; }
var result: i32 = 1;
var base: i32 = self;
var n: i32 = exp;
loop square() {
if (n == 0) { break square(); }
if ((n & 1) == 1) { result = result * base; }
base = base * base;
n = n >> 1;
continue square();
}
return result;
}
func i64.pow(self: i64, exp: i32) -> i64
Raises a 64-bit self to a non-negative power, by binary exponentiation.
Wraps modulo 2^64, and returns 0 for a negative exponent, exactly as i32.pow does.
- Parameters
- exp — the exponent; negative exponents yield
0
Source
func i64.pow(self: i64, exp: i32) -> i64 {
if (exp < 0) { return 0; }
var result: i64 = 1;
var base: i64 = self;
var n: i32 = exp;
loop square() {
if (n == 0) { break square(); }
if ((n & 1) == 1) { result = result * base; }
base = base * base;
n = n >> 1;
continue square();
}
return result;
}
func i32.gcd(self: i32, rhs: i32) -> i32
Greatest common divisor, by the binary (Stein's) algorithm.
Both operands are taken as magnitudes, so the result is always non-negative: (-12).gcd(18) is 6. gcd(0, n) is |n|, and gcd(0, 0) is 0.
Stein's algorithm rather than Euclid's because it needs no division: ctz and shifts only, which matters since i32.div_s is among the slowest instructions available.
At i32 minimum, whose magnitude 2^31 is not representable, the operand is treated as the unsigned value 2147483648 -- the honest reading of its bits, and the one that makes the result mathematically correct: (-2147483648).gcd(2) is 2. The result is still returned as an i32, so a gcd of exactly 2^31 (only possible when both operands are i32 minimum) comes back as -2147483648.
- Parameters
- rhs — the other operand
- Returns
- the largest integer dividing both, read as unsigned where noted above
- See also
i32.lcm
Source
func i32.gcd(self: i32, rhs: i32) -> i32 {
var a: i32 = self.abs();
var b: i32 = rhs.abs();
if (a == 0) { return b; }
if (b == 0) { return a; }
let shift: i32 = (a | b).ctz();
a = a >>> a.ctz();
loop reduce() {
b = b >>> b.ctz();
if (a.gt_u(b)) {
let t: i32 = a;
a = b;
b = t;
}
b = b - a;
if (b == 0) { break reduce(); }
continue reduce();
}
return a << shift;
}
func i32.lcm(self: i32, rhs: i32) -> i32
Least common multiple.
Divides before multiplying, so lcm is exact whenever the true result fits in i32; the naive a * b / gcd overflows for operands whose product does not fit even when the LCM itself would.
lcm(0, n) is 0. Overflow wraps, like *.
- Parameters
- rhs — the other operand
- See also
i32.gcd
Source
func i32.lcm(self: i32, rhs: i32) -> i32 {
if (self == 0 || rhs == 0) { return 0; }
let g: i32 = self.gcd(rhs);
return (self.abs() / g) * rhs.abs();
}
func i32.log2(self: i32) -> i32
Floor of the base-2 logarithm: the index of the highest set bit.
log2(0) is undefined mathematically; this returns -1, which is the value that makes 1 << (n.log2() + 1) a correct "round up to a power of two" for n == 0 as well.
Treats self as unsigned, so a negative input answers 31 rather than -1.
- Returns
- the floor of log2, or
-1 for zero
- See also
i32.is_pow2, i32.next_pow2
Source
func i32.log2(self: i32) -> i32 {
return 31 - self.clz();
}
func i32.log10(self: i32) -> i32
Floor of the base-10 logarithm: one less than the number of decimal digits.
Returns -1 for zero and for every negative input, mirroring log2's convention for an input with no logarithm.
- Returns
- the floor of log10, or
-1 when self <= 0
Source
func i32.log10(self: i32) -> i32 {
if (self <= 0) { return -1; }
var n: i32 = self;
var digits: i32 = 0;
loop count() {
n = n / 10;
if (n == 0) { break count(); }
digits = digits + 1;
continue count();
}
return digits;
}
func i32.is_pow2(self: i32) -> i32
Whether self is a power of two.
Zero is not a power of two, and neither is any negative value, both of which the naive (n & (n - 1)) == 0 gets wrong (it accepts zero and accepts i32 minimum).
- See also
i32.next_pow2
Source
func i32.is_pow2(self: i32) -> i32 {
return self > 0 && (self & (self - 1)) == 0;
}
func i32.next_pow2(self: i32) -> i32
The smallest power of two greater than or equal to self.
Returns 1 for every input at or below 1, since 1 = 2^0 is the smallest power of two available. Inputs above 2^30 have no representable answer and return 0, the wrapped value of 2^32 -- the same answer 1 << 32 would give.
- Returns
- the next power of two at or above
self
- See also
i32.is_pow2
Source
func i32.next_pow2(self: i32) -> i32 {
if (self <= 1) { return 1; }
return 1 << (32 - (self - 1).clz());
}
func i32.div_floor(self: i32, rhs: i32) -> i32
Euclidean division: the quotient rounded toward negative infinity.
Differs from /, which truncates toward zero: (-7).div_floor(2) is -4, while -7 / 2 is -3. Floor division is what indexing into a grid or bucketing a signed coordinate almost always wants.
Traps on a zero divisor, like /.
- Parameters
- rhs — the divisor
- See also
i32.mod_floor
Source
func i32.div_floor(self: i32, rhs: i32) -> i32 {
let q: i32 = self / rhs;
if ((self % rhs != 0) && ((self < 0) != (rhs < 0))) { return q - 1; }
return q;
}
func i32.mod_floor(self: i32, rhs: i32) -> i32
Euclidean remainder: the remainder of div_floor, carrying the divisor's sign.
Differs from %, which carries the dividend's sign: (-7).mod_floor(2) is 1, while -7 % 2 is -1. The result is always in [0, rhs) for a positive divisor, which is what makes it usable directly as an array index.
Traps on a zero divisor, like %.
- Parameters
- rhs — the divisor
- See also
i32.div_floor
Source
func i32.mod_floor(self: i32, rhs: i32) -> i32 {
let r: i32 = self % rhs;
if (r != 0 && ((r < 0) != (rhs < 0))) { return r + rhs; }
return r;
}
func i32.sat_add(self: i32, rhs: i32) -> i32
Addition that saturates at the i32 bounds instead of wrapping.
2147483647.sat_add(1) is 2147483647, where + would give -2147483648.
- Parameters
- rhs — the value to add
- See also
i32.sat_sub
Source
func i32.sat_add(self: i32, rhs: i32) -> i32 {
let sum: i32 = self + rhs;
if (((self ^ sum) & (rhs ^ sum)) < 0) {
return self < 0 ? -2147483648 : 2147483647;
}
return sum;
}
func i32.sat_sub(self: i32, rhs: i32) -> i32
Subtraction that saturates at the i32 bounds instead of wrapping.
- Parameters
- rhs — the value to subtract
- See also
i32.sat_add
Source
func i32.sat_sub(self: i32, rhs: i32) -> i32 {
let diff: i32 = self - rhs;
if (((self ^ rhs) & (self ^ diff)) < 0) {
return self < 0 ? -2147483648 : 2147483647;
}
return diff;
}
func i32.lerp(self: i32, to: i32, t: i32, scale: i32) -> i32
Linear interpolation between self and to, in fixed point.
t is a fraction over scale (pass scale = 1000 for per-mille, 256 for a binary fraction). Computes self + (to - self) * t / scale with the multiply before the divide, so precision is not lost to an early truncation.
Traps when scale is zero. Values outside 0..=scale extrapolate rather than clamp.
- Parameters
- to — the value at
t == scale
- t — the interpolation position
- scale — what
t is a fraction of
Source
func i32.lerp(self: i32, to: i32, t: i32, scale: i32) -> i32 {
return self + ((to - self) * t) / scale;
}
func i32.is_prime(self: i32) -> i32
Whether self is prime, by trial division against 2, 3, and then 6k±1.
Every value below 2 is composite by definition, including all negatives.
Trial division rather than a Miller-Rabin round: for the 32-bit range the loop runs at most ~7600 iterations, and it is exact with no witness set to get subtly wrong.
- Returns
1 when prime, 0 otherwise
Source
func i32.is_prime(self: i32) -> i32 {
if (self < 2) { return 0; }
if (self < 4) { return 1; }
if (self % 2 == 0) { return 0; }
if (self % 3 == 0) { return 0; }
let limit: i32 = self.isqrt();
var f: i32 = 5;
loop trial() {
if (f > limit) { break trial(); }
if (self % f == 0) { return 0; }
if (self % (f + 2) == 0) { return 0; }
f = f + 6;
continue trial();
}
return 1;
}