math.pow
base to the power exponent from + - * / only, exact by squaring for whole exponents, identical in every language.
1.0.0 · published 2026-10-03 by charlie · Anterra
Pinned by 16 tests, run in TypeScript, Python and Rust.
What it does
`base` to the power `exponent`, bit-identical in TypeScript, Python and Rust.
- **Whole-number exponents** are done by repeated squaring, so `pow(10, 3)` is exactly `1000` and `pow(-2, 3)` is `-8`. A negative whole exponent is the reciprocal. `exp(3 x ln 10)` would give 999.9999999999998. - **Other exponents** are `exp(exponent x ln(base))`, from `math.exp` and `math.ln`, which use only IEEE-exact operations. The error grows with the size of `exponent x ln(base)`: within a few dozen units in the last place for everyday arguments (cube roots, the sRGB gamma 2.4), which is far below any rounding a caller applies, and identical in every language, which is the point.
For example
pow(10, 3)→ 1,000 a whole exponent is exact: 10^3 is 1000, not 999.9999999999998pow(2, -2)→ 0.25 a negative whole exponentpow(-2, 3)→ -8 a negative base with an odd exponent
The function
The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.
pub fn pow(base: f64, exponent: f64) -> f64
| base | float | any finite number; negative only with a whole-number exponent |
| exponent | float | any finite number; 1/3 is a cube root, 2.4 the sRGB gamma |
| returns | float | bit-identical in every language |
Your code names it in one line, in the file that uses it
fune!(math.pow@^1); // then call pow(…)
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::math_exp::exp; ← from math.exp ^1.0.0 · built alongside by fune
use super::math_ln::ln; ← from math.ln ^1.0.0 · built alongside by fune
// Below this exponent of e the result is smaller than any normal double.
const SMALLEST: f64 = -708.0;
const LARGEST: f64 = 709.0;
/// base^exponent with the same bits in TypeScript, Python and Rust.
///
/// A whole-number exponent is done by repeated squaring, so 10^3 is exactly
/// 1000; any other is exp(exponent * ln(base)) from math.exp and math.ln.
/// `f64::powf` calls the platform's maths library, whose last bit may differ.
///
/// # Panics
/// Panics on a non-finite argument, zero to a negative power, a negative base
/// with a fractional exponent, or a result too large for a double.
pub fn pow(base: f64, exponent: f64) -> f64 {
if !base.is_finite() {
panic!("pow needs a finite base, received {}", base);
}
if !exponent.is_finite() {
panic!("pow needs a finite exponent, received {}", exponent);
}
if base == 0.0 {
if exponent < 0.0 {
panic!("zero cannot be raised to a negative power");
}
return if exponent == 0.0 { 1.0 } else { 0.0 };
}
if exponent.floor() == exponent {
let mut n = exponent.abs();
let mut b = base;
let mut result = 1.0;
while n > 0.0 {
if n % 2.0 == 1.0 {
result *= b;
}
n = (n / 2.0).floor();
if n > 0.0 {
b *= b;
}
}
if exponent < 0.0 {
result = 1.0 / result;
}
if !result.is_finite() {
panic!("pow result is too large to represent");
}
return result + 0.0;
}
if base < 0.0 {
panic!("a negative base needs a whole-number exponent");
}
let power = exponent * ln(base);
if power > LARGEST {
panic!("pow result is too large to represent");
}
if power < SMALLEST {
return 0.0;
}
exp(power)
}
pub fn fune_vector(args: &[Value]) -> Value {
Value::Float(pow(args[0].as_f64(), args[1].as_f64()))
}Install
fune build
With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and its 2 dependencies, pins them in fune.lock, downloads only the Rust package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. A crate’s build.rs runs it before every compile. Or pin a range in fune.project and build in one step:
fune add math.pow
The manifest, vectors and README with only the Rust implementation. Install it without the registry with fune add ./math.pow-1.0.0-rust.fune, or fetch it from a terminal with fune pull math.pow@1.0.0:rust.
The whole function, every language, is one file too: math.pow-1.0.0.fune, 10,336 bytes, sha256 cd57d65c28d5c41529f94f72488bf0eaae5a2fe9c3bc09339de550bc46cd5cc7. It installs into a project of any language.
Customise it in your app
The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.
before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.
// fune: before math.pow
after — your function gets the result and the arguments, and returns the final result.
// fune: after math.pow
replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.
// fune: replace math.exp in math.pow
// fune: replace math.ln in math.pow
step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show math.pow --steps.
// fune: step math.pow after <n|label>
Tests
A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.
| Case | Arguments | Expected | |
|---|---|---|---|
| a whole exponent is exact: 10^3 is 1000, not 999.9999999999998 | 10, 3 | → | 1,000 |
| a negative whole exponent | 2, -2 | → | 0.25 |
| a negative base with an odd exponent | -2, 3 | → | -8 |
| a negative base with an even exponent | -2, 2 | → | 4 |
| squaring keeps binary rounding: 1.1^2 | 1.1, 2 | → | 1.21 |
| a square root | 2, 0.5 | → | 1.414 |
| a cube root | 8, 0.333 | → | 2 |
| the sRGB gamma exponent | 0.5, 2.4 | → | 0.189 |
| anything to the power 0 is 1 | 7.5, 0 | → | 1 |
| zero to the power 0 is 1 | 0, 0 | → | 1 |
Show the other 6 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| zero to a positive power is 0 | 0, 2.5 | → | 0 |
| a result below the smallest normal double is 0 | 0.5, 2,000.5 | → | 0 |
| zero to a negative power is an error | 0, -1 | → | error: zero cannot be raised to a negative power |
| a negative base with a fractional exponent is an error | -8, 0.333 | → | error: a negative base needs a whole-number exponent |
| an overflowing whole power is an error | 10, 400 | → | error: pow result is too large to represent |
| an overflowing fractional power is an error | 10, 400.5 | → | error: pow result is too large to represent |
More from the author
Edge cases: `0^0` is 1, `0^positive` is 0, `0^negative` is an error. A negative base needs a whole-number exponent (the real answer does not exist otherwise). A result too large for a double is an error; one smaller than the smallest normal double (about 1e-308) is 0.
`**`, `Math.pow`, `math.pow` and `f64::powf` are not used because they come from each platform's maths library, whose last bit may differ.
Files
| Path | Bytes |
|---|---|
| README.md | 1,109 |
| impl/python.py | 1,876 |
| impl/rust.rs | 2,053 |
| impl/typescript.ts | 1,779 |
| vectors.json | 1,605 |