Excel 16.0 build 20430
Windows, 64-bit, CPU not recorded
129,326 captured inputs are indexed for this environment. Capture dates: 2026-09-29 to 2026-09-29. This is a reading count, not a comparison with another build.
Financial functions
Matches all 2,045 recorded answers
Recorded: 2,034 in the answer bank and 11 workbook cells. How we measure this · See the Rust code
| Language | Name in Excel |
|---|---|
| Arabic | قاعدة بيانات |
| Bulgarian | DB |
| Czech | DB |
| Danish | DB |
| German | DB |
| Greek | DB |
| English (Australia) | DB |
| English (United Kingdom) | DB |
| English (United States) | DB |
| Spanish | NOSQL |
| Estonian | DB |
| Finnish | DB |
| French | Base de données |
| Hebrew | DB |
| Croatian | DB |
| Hungarian | KCS2 |
| Indonesian | DB |
| Italian | DB |
| Japanese | DB |
| Korean | DB |
| Lithuanian | DB |
| Latvian | DB |
| Norwegian | DB |
| Dutch | DB |
| Polish | DB |
| Portuguese (Brazil) | NoSQL |
| Portuguese (Portugal) | DB |
| Romanian | DB |
| Russian | ФУО |
| Slovak | DB |
| Slovenian | DB |
| Serbian (Latin) | DB |
| Swedish | DB |
| Thai | DB |
| Turkish | DB |
| Ukrainian | DB |
| Vietnamese | DB |
| Chinese (Simplified) | DB |
| Chinese (Hong Kong) | DB |
| Chinese (Traditional) | DB |
Names from Microsoft's support site.
DB(cost, salvage, life, period, [month])
DB(cost, salvage, life, period, [month]) returns the depreciation charge for one period
under the fixed-declining-balance method.
"Fixed" means the rate is fixed, not the charge: a constant fraction of the remaining book value is written off each period, so charges fall geometrically. Microsoft's Learn page states the whole schedule in three formulas.
The ordinary period:
charge = (cost − total depreciation from prior periods) × rate where rate = 1 − (salvage / cost) ^ (1 / life), rounded to three decimal places
The first period, prorated by the number of months the asset was held in the first year:
charge = cost × rate × month / 12
And the last period — the extra stub period that exists only when month < 12:
charge = ((cost − total depreciation from prior periods) × rate × (12 − month)) / 12
Those three formulas are quoted from Microsoft. Together they define a schedule of life
periods when month is 12, and life + 1 periods otherwise: the first and last are partial
years that sum to one full year of depreciation.
The derivation of rate is the idea worth pausing on. Solving
salvage = cost × (1 − rate)^life for rate gives exactly the formula above: it is the constant
rate that would depreciate the asset from cost down to precisely salvage over life periods.
DB is therefore the declining-balance method that targets the salvage value, rather than one
that stops when it reaches it.
Except that it does not quite reach it, because of the rounding.
DB(cost, salvage, life, period, [month])
| Argument | Meaning (Microsoft's wording) | Required? |
|---|---|---|
cost | "The initial cost of the asset." | Required |
salvage | "The value at the end of the depreciation (sometimes called the salvage value of the asset)." | Required |
life | "The number of periods over which the asset is being depreciated (sometimes called the useful life of the asset)." | Required |
period | "The period for which you want to calculate the depreciation. Period must use the same units as life." | Required |
month | "The number of months in the first year. If month is omitted, it is assumed to be 12." | Optional, defaults to 12 |
period is 1-based. Period 1 is the first (possibly prorated) year. This is the opposite of
AMORLINC and AMORDEGRC, which number depreciation
periods from 0, and mixing the two conventions in one model is a reliable way to produce a
plausible wrong schedule.
month is not a date. It is a count of months in the first year, so it prorates by
twelfths regardless of the calendar. DB therefore needs no day-count basis and has none —
which is exactly why it is not interchangeable with the French depreciation pair, which prorate
by an actual date interval.
month < 12 adds a period. The schedule then runs to life + 1, with the last period
taking the complementary (12 − month)/12 fraction. Asking for period = life + 1 with
month = 12 is out of range; asking for it with month = 11 is the stub year.
salvage and cost enter only through their ratio, so scaling both by the same factor
leaves rate unchanged and scales every charge linearly.
Numeric slots follow the shared model; see Coercion and lifting.
rate is rounded to three decimal places before any charge is computed. Microsoft documents
this explicitly, and it is not a display convention: the rounded rate is what the schedule uses,
so it changes every number the function returns.
Two consequences follow, and both are visible in ordinary use:
salvage. The exact rate would; a rate rounded to three
decimals will not. A DB schedule summed over its whole life leaves a residual book value
that is above or below salvage depending on which way the rounding went. Anyone reconciling
a DB column against cost − salvage will find a discrepancy, and it is by construction.salvage, cost or life slightly
and rate may not change at all — or may jump by 0.001, moving every subsequent charge. DB
is therefore piecewise-constant in a way none of its siblings are, and small input errors
produce either no error or a discontinuous one.Returns a Number: the charge for the requested period, in the units of cost.
salvage = 0. The ratio is 0, 0^(1/life) is 0, and the rate is exactly 1 — so the first
period writes off the entire asset (prorated by month) and later periods charge nothing. The
formula is doing what it says; the method simply has no meaning when the target salvage is
zero. Microsoft's page does not flag this. If Excel special-cases it, that is a divergence
worth recording.salvage close to cost gives a rate near 0, which rounds to 0.000 and produces a
schedule of zeros. Again the rounding, not the mathematics.period = life + 1 with month < 12 is the stub year and uses the last-period formula.life is accepted; the "last full regular period" is taken as its floor, which
moves where the stub-year formula applies.Microsoft's Learn page documents no error conditions for this function — it carries the
parameter table and the three formulas, and stops. There is no published statement of what
happens with a negative cost, a month of 13, or a period beyond the schedule.
Note that salvage > cost is not rejected: the ratio exceeds 1, the power is above 1, and
the rate goes negative — producing negative depreciation, that is, appreciation. Whether Excel
admits that is unverified, and the documentation is silent.
rate dominates everything. Three decimal places is coarse: on a
ten-period asset the difference between a rate of 0.2065 and its rounded 0.207 compounds
through the whole schedule. Any implementation must round at exactly the same point — after
forming 1 − ratio^(1/life), before the first charge — and with the same tie rule.ratio^(1/life) is a general power and is the only transcendental in the function. It
is evaluated as exp(ln(ratio)/life) on most platforms, so DB inherits whatever pow the
host provides — the same dependency the Handbook's research record traced through the bond and
distribution families to a single shared library routine. The rounding to three decimals
mercifully hides the last-bit differences, but only until the fourth decimal sits near a tie.floor(life), so a fractional life moves it. This is
an implementation decision with no documentary support.Quoted from the public repository DnaCalc/DnaCalc, crates calc-func and calc-fml, at commit 9e1884369a67. Comments are the source's own. A kernel shared with other functions appears once, on its module page.
DB · 6 lines · calc-func/src/catalogue/financial.rs on GitHub23 db_fn "DB" (4, 5) [V "cost", V "salvage", V "life", V "period", V "month"] |s, a, sv| {
// W111 independent error-order controls: earlier required arguments precede month.
let (cost, salvage, life, period) = (num(s, a, 0)?, num(s, a, 1)?, num(s, a, 2)?, num(s, a, 3)?);
let month = if a.len() > 4 { num(s, a, 4)? } else { 12.0 };
out(s, dep::db_kernel(cost, salvage, life, period, month))
}
round_to_3_places · 15 lines · calc-func/src/ported/depreciation_family.rs on GitHub/// ROUND(x, 3), by the catalogue's ROUND rule: the exact decimal reading of `x`, rounded half away
/// from zero (OxFunc calls its round_fn::round_kernel here).
fn round_to_3_places(x: f64) -> f64 {
use crate::decimal::{Direction, ExactDecimal};
if x == 0.0 {
return 0.0;
}
match ExactDecimal::read(x).round_at(-3, Direction::Nearest) {
None => x,
Some(rounded) => rounded
.text()
.parse()
.expect("an exact decimal reads as a binary64"),
}
}
db_rate · 46 lines · calc-func/src/ported/depreciation_family.rs on GitHubfn db_rate(cost: f64, salvage: f64, life: f64) -> Result<f64, WorksheetErrorCode> {
let ratio = crate::ported::excel_numeric::excel_x87_div(salvage, cost);
// W111 ratio/power overflow and subnormal probes publish rate=1, rather
// than propagating an intermediate numeric error. This is internal DB
// arithmetic; the source Value2 inputs remain exactly as captured.
let ratio = if !ratio.is_finite() || ratio.abs() < f64::MIN_POSITIVE {
0.0
} else {
ratio
};
let exponent = crate::ported::excel_numeric::excel_x87_div(1.0, life);
let integer_power = exponent.fract() == 0.0 && exponent < u32::MAX as f64;
let power = if integer_power {
// DB's integer dispatch is narrower than the worksheet POWER wrapper.
let value = depreciation_integer_power(ratio, exponent as u32);
if value.is_finite() {
value
} else {
0.0
}
} else if ratio == 0.0 {
0.0
} else {
// Above the unsigned-count sentinel, even exact integer exponents use
// the positive power chain. Rate-half probes distinguish it from the
// worksheet wrapper's wider repeated-multiplication integer branch.
crate::ported::excel_numeric::excel_pow_positive(ratio, exponent)
};
// An overflowing exp/log product reaches NaN in the internal power chain.
// DB publishes a zero rate on that route, distinct from an ordinary power
// overflow/error or an underflowed finite power (W111 extreme-life probes).
if power.is_nan() {
return Ok(0.0);
}
if power.is_infinite() {
return Err(WorksheetErrorCode::Num);
}
let power = if power.abs() < f64::MIN_POSITIVE {
0.0
} else {
power
};
// DB uses Excel decimal ROUND, including its 15-significant-digit input
// staging, not binary (rate*1000+0.5).floor()/1000.
Ok(round_to_3_places(1.0 - power))
}
db_kernel · 71 lines · calc-func/src/ported/depreciation_family.rs on GitHubpub fn db_kernel(
cost: f64,
salvage: f64,
life: f64,
period: f64,
month: f64,
) -> Result<f64, WorksheetErrorCode> {
validate_finite(&[cost, salvage, life, period, month])?;
// The period and month are truncated, but life remains fractional. Original
// positive periods below one still request the initial depreciation.
let month = month.trunc();
// The host first saturates a nonnegative count to u32, then interprets that
// word as signed. W111 boundary probes distinguish this from an i32 cast:
// 2^31 becomes MIN, while counts above u32::MAX become -1.
let whole_period = (period as u32) as i32;
let whole_life = (life as u32) as i32;
if cost < 0.0
|| salvage < 0.0
|| life <= 0.0
|| period <= 0.0
|| month < 1.0
|| month > 12.0
|| i64::from(whole_period) > i64::from(whole_life) + if month < 12.0 { 1 } else { 0 }
{
return Err(WorksheetErrorCode::Num);
}
if cost == 0.0 {
return Ok(0.0);
}
let rate = db_rate(cost, salvage, life)?;
if rate == 0.0 {
return Ok(0.0);
}
// These store boundaries are observed by W111 DB probes. Replacing the
// book recurrence with cost minus a running total changes final bits.
let mut dep = crate::ported::excel_numeric::excel_x87_mul(
crate::ported::excel_numeric::excel_x87_mul(cost, rate),
month / 12.0,
);
// Each depreciation amount is published before it changes the book.
// A subnormal first/regular amount is zero even if subsequent negative-rate
// growth could otherwise amplify that difference (W111 minimum-normal inputs).
dep = publish_depreciation(dep)?;
let mut book = crate::ported::excel_numeric::excel_x87_sub(cost, dep);
let regular_periods = whole_period.min(whole_life).max(0) as usize;
for p in 2..=regular_periods {
dep = publish_depreciation(crate::ported::excel_numeric::excel_x87_mul(book, rate))?;
if dep == 0.0 {
break;
}
book = crate::ported::excel_numeric::excel_x87_sub(book, dep);
if book == 0.0 {
if p < regular_periods {
dep = 0.0;
}
break;
}
if !book.is_finite() {
return Err(WorksheetErrorCode::Num);
}
}
if month < 12.0 && i64::from(whole_period) == i64::from(whole_life) + 1 {
// The final partial year's quotient and rate product are separate
// published amounts too: later multiplication must not recover a
// subnormal intermediate (W111 minimum-normal final-year probes).
let quotient = publish_depreciation(book / 12.0)?;
let product = publish_depreciation(crate::ported::excel_numeric::excel_x87_mul(quotient, rate))?;
dep = product * (12.0 - month);
}
publish_depreciation(dep)
}
low32 decimalNatural::zero decimalNatural::from_u64 decimalNatural::normalize decimalNatural::is_zero decimalNatural::mul_add_small decimalNatural::times_pow5 decimalNatural::times_pow2 decimalExactDecimal::read decimalExactDecimal::binary decimalvalidate_finite ported/depreciation_familypublish_depreciation ported/depreciation_familydepreciation_integer_power ported/depreciation_familybackend::sqrt ported/excel_numericbinary_with ported/excel_numericexcel_pow_positive ported/excel_numericexcel_pow_chain ported/excel_numericexcel_x87_sub ported/excel_numericexcel_x87_mul ported/excel_numericexcel_x87_div ported/excel_numericmul ported/excel_numeric/x87sub ported/excel_numeric/x87div ported/excel_numeric/x87pow2_positive ported/excel_numeric/x87_chainspow2 ported/excel_numeric/x87_chainsexp_ext ported/excel_numeric/x87_chainsexp_with ported/excel_numeric/x87_chainsexcel_exp ported/excel_numeric/x87_chainslog_with ported/excel_numeric/x87_chainsexcel_log ported/excel_numeric/x87_chainsNo implementation has been published with a Handbook test suite for this function.
The recorded environments below distinguish a reading from a same-input comparison. Counts describe captured inputs in each environment; overlapping captures and comparisons are not totals of unique worksheet calls.
| Excel 16.0 build | Platform and CPU | Captured inputs |
|---|---|---|
| 20430 | Windows, 64-bit, CPU not recorded | 129,326 |
The record contains no same-input comparison between these environments. Reading different inputs on different builds does not establish either a version change or cross-build agreement.
Recorded environments and comparison details.
Windows, 64-bit, CPU not recorded
129,326 captured inputs are indexed for this environment. Capture dates: 2026-09-29 to 2026-09-29. This is a reading count, not a comparison with another build.
DDB takes the rate as a factor and derives nothing; DB derives the rate from
the salvage ratio and rounds it. DDB clamps each charge so the book value never falls below
salvage; DB has no clamp and relies on the rate to land near it. DDB has no
first-year proration; DB has month.VDB — the general declining-balance function, with a rate factor, a period range, and
an optional switch to straight line. The most flexible member of the family.SLN — straight line, (cost − salvage)/life, the flat comparison every declining-balance
schedule is measured against.SYD — sum-of-years-digits, an accelerated schedule that does exhaust cost − salvage
exactly, unlike DB.month count and with 0-based periods.DDB (above), and with the assumption that a DB schedule sums to
cost − salvage. It does not.No named variants are recorded yet.
Suggest a correction or contribute an implementation · For agents