health.bsa Unreviewed
Body surface area in m² by the Mosteller and the DuBois & DuBois formulas, to 2 decimal places.
1.0.1 · published 2026-10-03 by charlie · Anterra
Pinned by 16 tests, run in TypeScript, Python and Rust.
Unreviewed. Not for clinical use; regulatory review pending. This capability’s implementations agree in every language and pass its published test vectors, which were worked out from the official sources cited. But no qualified clinician or pharmacist has yet checked those vectors, or confirmed that the capability covers the cases it claims. Treat it as a draft. Do not use it for real people, money or decisions without your own expert review. Once a qualified reviewer signs off, this notice is replaced with their name, qualification and the date. Each new version needs fresh sign-off.
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
bodySurfaceArea(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)bodySurfaceArea(80, 180)→ mosteller 2, du bois 2 180 cm, 80 kg: Mosteller is exactly √4 = 2.00; DuBois 1.9964 rounds to 2.00bodySurfaceArea(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.
export function bodySurfaceArea(weightKg: number, heightCm: number): BodySurfaceArea
| weightKg | float | body weight in kilograms, 1 to 650 |
| heightCm | 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. */
export interface BodySurfaceArea {
/** m², √(height cm × weight kg ÷ 3600), rounded half away from zero to 2 places */
readonly mosteller: number;
/** m², 71.84 × W^0.425 × H^0.725 cm² ÷ 10000, rounded half away from zero to 2 places */
readonly duBois: number;
}
Your code names it in one line, in the file that uses it
import { bodySurfaceArea } from "#fune/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 { pow } from "./math_pow.ts"; ← from math.pow ^1.0.0 · built alongside by fune
import { roundFloat } from "./math_round_float.ts"; ← from math.round-float ^1.0.0 · built alongside by fune
import { type BodySurfaceArea } from "./health_bsa_types.ts";
function checkRange(name: string, value: number, low: number, high: number, unit: string): void {
if (typeof value !== "number" || !Number.isFinite(value) || value < low || value > high) {
throw new RangeError(`${name} must be a number from ${low} to ${high} ${unit}, received ${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 Math.pow or Math.sqrt,
* so the three languages agree to the bit before rounding.
*/
export function bodySurfaceArea(weightKg: number, heightCm: number): BodySurfaceArea {
checkRange("weightKg", weightKg, 1, 650, "kg");
// Height in metres (1.75) is the commonest unit slip.
checkRange("heightCm", heightCm, 30, 272, "cm");
// Mosteller (1987): BSA (m²) = ([height (cm) × weight (kg)] / 3600)^½.
const mosteller = pow((heightCm * weightKg) / 3600, 0.5);
// DuBois & DuBois (1916): A = W^0.425 × H^0.725 × 71.84, A in cm².
const duBois = (pow(weightKg, 0.425) * pow(heightCm, 0.725) * 71.84) / 10000;
return { mosteller: roundFloat(mosteller, 2), duBois: roundFloat(duBois, 2) };
}Install
fune build
With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and its 2 dependencies, pins them in fune.lock, downloads only the TypeScript 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 TypeScript implementation. Install it without the registry with fune add ./health.bsa-1.0.1-typescript.fune, or fetch it from a terminal with fune pull health.bsa@1.0.1:typescript.
The whole function, every language, is one file too: health.bsa-1.0.1.fune, 13,763 bytes, sha256 fe964241429628530d99653abd5ac16c3aa6ebad194a08439b4b61137669358c. 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
## Before you rely on this
**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 above, 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.
**Not for clinical use; regulatory review pending.** Whether publishing this capability makes it a medical device is under regulatory review. Until that is settled it is a developer library only.
**Unreviewed.** This capability's implementations agree in every language and pass its published test vectors, which were worked out from the official sources cited. But no qualified clinician or pharmacist has yet checked those vectors, or confirmed that the capability covers the cases it claims. Treat it as a draft. Do not use it for real people, money or decisions without your own expert review. Once a qualified reviewer signs off, this notice is replaced with their name, qualification and the date. Each new version needs fresh sign-off.
1.0.1 marks it unreviewed (not for clinical use; regulatory review pending). The code and the tests are unchanged.
Files
| Path | Bytes |
|---|---|
| README.md | 4,278 |
| impl/python.py | 1,411 |
| impl/rust.rs | 2,045 |
| impl/typescript.ts | 1,371 |
| vectors.json | 2,071 |