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.
def body_surface_area(weight_kg: float, height_cm: float) -> 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
@dataclass(frozen=True)
class BodySurfaceArea:
"""Both formulas, so a caller uses the one its protocol names and never mixes them."""
#: m², √(height cm × weight kg ÷ 3600), rounded half away from zero to 2 places
mosteller: float
#: m², 71.84 × W^0.425 × H^0.725 cm² ÷ 10000, rounded half away from zero to 2 places
du_bois: float
Your code names it in one line, in the file that uses it
from fune.health.bsa import body_surface_area # health.bsa@^1
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import math
from .health_bsa_types import BodySurfaceArea
from .math_pow import pow ← from math.pow ^1.0.0 · built alongside by fune
from .math_round_float import round_float ← from math.round-float ^1.0.0 · built alongside by fune
def _check_range(name: str, value: float, low: float, high: float, unit: str) -> None:
ok = not isinstance(value, bool) and isinstance(value, (int, float)) and math.isfinite(value)
if not ok or value < low or value > high:
raise ValueError("%s must be a number from %s to %s %s, received %r" % (name, low, high, unit, value))
def body_surface_area(weight_kg: float, height_cm: float) -> BodySurfaceArea:
"""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 ** or
math.sqrt, so the three languages agree to the bit before rounding."""
_check_range("weightKg", weight_kg, 1, 650, "kg")
# Height in metres (1.75) is the commonest unit slip.
_check_range("heightCm", height_cm, 30, 272, "cm")
w = float(weight_kg)
h = float(height_cm)
# Mosteller (1987): BSA (m2) = ([height (cm) x weight (kg)] / 3600)^1/2.
mosteller = pow((h * w) / 3600, 0.5)
# DuBois & DuBois (1916): A = W^0.425 x H^0.725 x 71.84, A in cm2.
du_bois = (pow(w, 0.425) * pow(h, 0.725) * 71.84) / 10000
return BodySurfaceArea(mosteller=round_float(mosteller, 2), du_bois=round_float(du_bois, 2))Install
fune build
With that line in your source, in a Python project (language python in fune.project), fune build resolves it and its 2 dependencies, pins them in fune.lock, downloads only the Python 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. Or pin a range in fune.project and build in one step:
fune add health.bsa
The manifest, vectors and README with only the Python implementation. Install it without the registry with fune add ./health.bsa-1.0.0-python.fune, or fetch it from a terminal with fune pull health.bsa@1.0.0:python.
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 |