health.bsa
Body surface area in m² by the Mosteller and the DuBois & DuBois formulas, to 2 decimal places.
1.0.0 (not the latest) · published 2026-10-03 by charlie · Anterra
Pinned by 16 tests, run in TypeScript, Python and Rust.
Not professional advice. This capability calculates health figures from published rules. It is a software component for developers, not medical advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed in its README, and have a clinician or pharmacist review how you use it, before anyone relies on the output. Provided “as is” under its licence, without warranty.
Not a medical device. It is not intended to diagnose, treat or support clinical decisions about any individual. Anyone building it into clinical software is responsible for that software’s regulatory status, and must validate it under their own clinical governance.
What it does
Status: needs review and sign-off by a qualified clinician before it is published. Not a medical device; for decision support only; always follow local clinical guidelines.
Body surface area (BSA) in square metres from weight and height, by the two formulas in common clinical use, each rounded half away from zero to two decimal places:
For example
body_surface_area(60, 150)→ mosteller 1.58, du bois 1.55 DuBois & DuBois's own worked example: 150 cm, 60 kg is 1.55 m² (Mosteller 1.58)body_surface_area(80, 180)→ mosteller 2, du bois 2 180 cm, 80 kg: Mosteller is exactly √4 = 2.00; DuBois 1.9964 rounds to 2.00body_surface_area(70, 170)→ mosteller 1.82, du bois 1.81 170 cm, 70 kg: the formulas disagree in the second place
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 body_surface_area(weight_kg: f64, height_cm: f64) -> BodySurfaceArea
| weight_kg | float | body weight in kilograms, 1 to 650 |
| height_cm | float | height in centimetres, 30 to 272; not metres |
| returns | BodySurfaceArea |
The type it declares, generated into your project
/// Both formulas, so a caller uses the one its protocol names and never mixes them.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BodySurfaceArea {
/// m², √(height cm × weight kg ÷ 3600), rounded half away from zero to 2 places
pub mosteller: f64,
/// m², 71.84 × W^0.425 × H^0.725 cm² ÷ 10000, rounded half away from zero to 2 places
pub du_bois: f64,
}
Your code names it in one line, in the file that uses it
fune!(health.bsa@^1); // then call body_surface_area(…)
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_pow::pow; ← from math.pow ^1.0.0 · built alongside by fune
use super::math_round_float::round_float; ← from math.round-float ^1.0.0 · built alongside by fune
fn check_range(name: &str, value: f64, low: f64, high: f64, unit: &str) {
if !value.is_finite() || value < low || value > high {
panic!("{} must be a number from {} to {} {}, received {}", name, low, high, unit, value);
}
}
/// Body surface area by both published formulas. They differ by up to a few
/// per cent, and a dose protocol names one, so both are returned rather than a
/// silent default. Powers go through math.pow, never `powf` or `sqrt`, so the
/// three languages agree to the bit before rounding.
///
/// # Panics
/// Panics when the weight or height is outside its range.
pub fn body_surface_area(weight_kg: f64, height_cm: f64) -> BodySurfaceArea {
check_range("weightKg", weight_kg, 1.0, 650.0, "kg");
// Height in metres (1.75) is the commonest unit slip.
check_range("heightCm", height_cm, 30.0, 272.0, "cm");
// Mosteller (1987): BSA (m²) = ([height (cm) × weight (kg)] / 3600)^½.
let mosteller = pow((height_cm * weight_kg) / 3600.0, 0.5);
// DuBois & DuBois (1916): A = W^0.425 × H^0.725 × 71.84, A in cm².
let du_bois = (pow(weight_kg, 0.425) * pow(height_cm, 0.725) * 71.84) / 10000.0;
BodySurfaceArea { mosteller: round_float(mosteller, 2), du_bois: round_float(du_bois, 2) }
}
pub fn body_surface_area_to_value(result: &BodySurfaceArea) -> Value {
Value::obj(vec![("mosteller", Value::Float(result.mosteller)), ("duBois", Value::Float(result.du_bois))])
}
fn number_arg(value: &Value, name: &str, low: f64, high: f64, unit: &str) -> f64 {
match value {
Value::Int(_) | Value::Float(_) => value.as_f64(),
_ => panic!("{} must be a number from {} to {} {}, received {:?}", name, low, high, unit, value),
}
}
pub fn fune_vector(args: &[Value]) -> Value {
body_surface_area_to_value(&body_surface_area(
number_arg(&args[0], "weightKg", 1.0, 650.0, "kg"),
number_arg(&args[1], "heightCm", 30.0, 272.0, "cm"),
))
}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 health.bsa
The manifest, vectors and README with only the Rust implementation. Install it without the registry with fune add ./health.bsa-1.0.0-rust.fune, or fetch it from a terminal with fune pull health.bsa@1.0.0:rust.
The whole function, every language, is one file too: health.bsa-1.0.0.fune, 12,035 bytes, sha256 81cf3f02076ab9cb46048c8c90ba49ad1655d6fda3d2ee260a6db0b00e886756. 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 health.bsa
after — your function gets the result and the arguments, and returns the final result.
// fune: after health.bsa
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.pow in health.bsa
// fune: replace math.round-float in health.bsa
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 health.bsa --steps.
// fune: step health.bsa 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 | |
|---|---|---|---|
| DuBois & DuBois's own worked example: 150 cm, 60 kg is 1.55 m² (Mosteller 1.58) | 60, 150 | → | mosteller 1.58, du bois 1.55 |
| 180 cm, 80 kg: Mosteller is exactly √4 = 2.00; DuBois 1.9964 rounds to 2.00 | 80, 180 | → | mosteller 2, du bois 2 |
| 170 cm, 70 kg: the formulas disagree in the second place | 70, 170 | → | mosteller 1.82, du bois 1.81 |
| 175 cm, 70 kg: here DuBois is the larger | 70, 175 | → | mosteller 1.84, du bois 1.85 |
| 152 cm, 45 kg | 45, 152 | → | mosteller 1.38, du bois 1.38 |
| 190 cm, 100 kg | 100, 190 | → | mosteller 2.3, du bois 2.28 |
| 165 cm, 150 kg: in obesity DuBois is 0.17 m² lower | 150, 165 | → | mosteller 2.62, du bois 2.45 |
| a child: 110 cm, 20 kg; DuBois 0.77502 is just above the 0.775 tie | 20, 110 | → | mosteller 0.78, du bois 0.78 |
| a newborn: 50 cm, 3.5 kg | 3.5, 50 | → | mosteller 0.22, du bois 0.21 |
| the lowest accepted weight and height | 1, 30 | → | mosteller 0.09, du bois 0.08 |
Show the other 6 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| the highest accepted weight and height | 650, 272 | → | mosteller 7.01, du bois 6.56 |
| height in metres is refused | 70, 1.75 | → | error: heightCm must be a number from 30 to 272 cm |
| zero weight is refused | 0, 170 | → | error: weightKg must be a number from 1 to 650 kg |
| negative height is refused | 70, -170 | → | error: heightCm must be a number from 30 to 272 cm |
| a weight in grams is refused | 70,000, 170 | → | error: weightKg must be a number from 1 to 650 kg |
| a missing weight is refused | —, 170 | → | error: weightKg must be a number from 1 to 650 kg |
More from the author
- **Mosteller**: BSA (m²) = √( height (cm) × weight (kg) ÷ 3600 ) - **DuBois & DuBois**: A = W^0.425 × H^0.725 × 71.84, with A in cm², W in kg and H in cm; divided by 10 000 for m² (the familiar 0.007184 × W^0.425 × H^0.725)
## Why both
The two disagree by up to a few per cent, more in obesity (150 kg at 165 cm is 2.62 m² by Mosteller and 2.45 m² by DuBois). Chemotherapy verification standards accept either but insist on consistency: a protocol, a prescriber and a pharmacist checking the prescription must all use the same formula. So both are returned, named, and nothing picks one silently.
## Edge cases
- Weight 1 to 650 kg, height 30 to 272 cm; anything else is refused, never clamped. Height in metres (1.75) is the usual slip. These ranges are guard rails for a clinician to confirm, not part of either formula. - DuBois & DuBois derived their formula from nine subjects, one a child; paediatric protocols often specify Mosteller (or another formula) instead. - Two decimal places is the precision BSA is prescribed at. Some units round further (to 0.1 m²) for dose banding; that is the caller's step. - Powers use `math.pow` (and the square root is `math.pow(x, 0.5)`), not the platform library, so the three languages agree to the bit (`floats exact`).
## Sources
- Du Bois D, Du Bois EF. *Clinical calorimetry, tenth paper: A formula to estimate the approximate surface area if height and weight be known.* Arch Intern Med 1916;17(6):863-871. The equation, the constant 71.84 and the worked example (150 cm, 60 kg, 1.55 m²) were read from the scanned original: https://archive.org/details/sim_jama-internal-medicine_1916-06-15_17_6 - Mosteller RD. *Simplified calculation of body-surface area.* N Engl J Med 1987;317(17):1098. doi:10.1056/NEJM198710223171717. The letter itself is behind a paywall and was not retrieved; the equation and constant 3600 were confirmed from the NHS Thames Valley Cancer Network, *Verification standards SOP* (2015), which states "BSA (m²) = ([Height(cm) x Weight(kg)]/3600)½" citing the letter, and that "the DuBois or Mosteller formulae are accepted as the standard BSA nomogram for adults": https://thamesvalleycanceralliance.nhs.uk/wp-content/uploads/2022/03/TVCN-Verification-SOP-November-2015.pdf
Files
| Path | Bytes |
|---|---|
| README.md | 2,661 |
| impl/python.py | 1,411 |
| impl/rust.rs | 2,045 |
| impl/typescript.ts | 1,371 |
| vectors.json | 2,071 |