Math and trigonometry functions

MMULT

Matches all 7 recorded answers

Recorded: 7 workbook cells. How we measure this · See the Rust code

Local names in 40 languages
LanguageName in Excel
ArabicMMULT
BulgarianMMULT
CzechMMULT
DanishMPRODUKT
GermanMMULT
GreekMMULT
English (Australia)MMULT
English (United Kingdom)MMULT
English (United States)MMULT
SpanishMMULT
EstonianMMULT
FinnishMKERRO
FrenchPRODUITMAT
HebrewMMULT
CroatianMMULT
HungarianMSZORZAT
IndonesianMMULT
ItalianMMULT
JapaneseMMULT
KoreanMMULT
LithuanianMMULT
LatvianMMULT
NorwegianMMULT
DutchPRODUCTMAT
PolishMACIERZ.ILOCZYN
Portuguese (Brazil)MMULT
Portuguese (Portugal)MATRIZ.MULT
RomanianMMULT
RussianМУМНОЖ
SlovakMMULT
SlovenianMMULT
Serbian (Latin)MMULT
SwedishMMULT
ThaiMMULT
TurkishDÇARP işlevi
UkrainianMMULT
VietnameseMMULT
Chinese (Simplified)MMULT
Chinese (Hong Kong)MMULT
Chinese (Traditional)MMULT

Names from Microsoft's support site.

What it does

MMULT(array1, array2)

MMULT(array1, array2) returns the matrix product of two matrices.

(AB)ᵢⱼ = Σₖ₌₁ⁿ aᵢₖ · bₖⱼ

Each entry of the result is a dot product: one row of the left matrix against one column of the right. Microsoft's page states the shape rule that makes this well defined:

"The number of columns in array1 must be the same as the number of rows in array2, and both arrays must contain only numbers."

So an m×n matrix times an n×p matrix gives an m×p result, and the shared dimension n — the length of every dot product — vanishes from the answer's shape.

Algebraic properties, all of which readers use and some of which surprise them:

  • Associative: (AB)C = A(BC). Not commutative: AB ≠ BA in general, and the two products may not even have the same shape.
  • Distributive over addition, and compatible with scalar multiplication.
  • MUNIT is the identity: AI = IA = A at the matching size.
  • det(AB) = det A · det B — the bridge to MDETERM, and the cheapest metamorphic test available for this family.
  • (AB)ᵀ = BᵀAᵀ and (AB)⁻¹ = B⁻¹A⁻¹ — both reverse the order.
  • Matrix multiplication is exactly function composition for the corresponding linear maps, which is why the order reverses and why it fails to commute.

Domain and range. Domain: any conformable pair of numeric matrices. Range: numeric matrices. The product is a polynomial in the entries — continuous, smooth, no poles, no branch cuts. As with MDETERM, every difficulty here is numerical rather than analytic; unlike MINVERSE, there is no singularity to approach.

Arguments

ArgumentMeaningRequired
array1The left factor.Yes
array2The right factor.Yes

Both required; the declared arity is exactly two. Microsoft's page notes that both "can be given as cell ranges, array constants, or references".

Result shape and entry. MMULT returns an array with the row count of array1 and the column count of array2. Microsoft's page gives the entry mechanics: in current Microsoft 365 the formula spills from a single cell, and in earlier versions the output range must be selected first and the formula confirmed with Ctrl+Shift+Enter.

Try it

Input cells (optional)

Example: {"B2": 3, "B3": 4}. A1 holds the formula. Other cells need values before a formula can read them.

Enter a formula, then calculate.

Runs locally in your browser with OneCalc. Its answer may differ from Excel. Use English function names and commas between arguments.

Quirks

Result and edge cases

Returns Array of Number.

  • A logical argument and numeric text both produce #VALUE!, consistent with the documented "must contain only numbers" rule for text and undocumented for logicals — the same family-wide refusal noted on MDETERM.
  • An empty range produces #VALUE!, matching the documented empty-cell rule.
  • Two inline 2×2 arrays produce the expected 2×2 product.

Errors

ErrorDocumented condition
#VALUE!"Any cells are empty or contain text"
#VALUE!"The number of columns in array1 is different from the number of rows in array2"

Error values inside either array propagate; the family declares a reduction error-collapse profile, so competing errors are folded rather than surfacing independently (coercion and lifting).

Microsoft's page documents no #NUM! for MMULT, and no overflow condition at all. Given that a dot product of large entries overflows readily, that is a gap rather than a guarantee.

How to implement

Numerical notes

MMULT has no pivoting, no division, and no singularity. Every last bit it produces is decided by one thing: the order in which the dot products accumulate.

The error bound

For a single dot product of length n computed by sequential accumulation, the standard bound (Higham, Accuracy and Stability of Numerical Algorithms, chapter 3) is

|fl(xᵀy) − xᵀy| ≤ γₙ · |x|ᵀ|y|,      γₙ = n·u / (1 − n·u)

with u the unit roundoff. Two things follow immediately, and they are the whole practical story:

  1. The bound grows with n, the shared dimension — not with the size of the result. A product of two 2×500 and 500×2 matrices has four entries and five hundred terms each; it is a harder computation than a 20×20 by 20×20 product with four hundred entries.
  2. The bound is in terms of |x|ᵀ|y|, not |xᵀy|. When the terms of the dot product have mixed signs and largely cancel, the relative error in the answer is unbounded. This is the matrix version of catastrophic cancellation, and it is why differencing data before multiplying is dangerous.

Accumulation order changes the answer, and every implementation chooses one

Floating-point addition is not associative, so:

  • Sequential left-to-right, pairwise, and blocked accumulation give different bits.
  • A fused multiply-add contracts a*b + c into one rounding instead of two, changing the result — usually improving it, always changing it.
  • Cache-blocked and vectorised kernels reorder the sum by construction; a BLAS dgemm and a naive triple loop will not agree to the last bit.
  • Strassen-style fast algorithms change the arithmetic entirely and have a different, weaker error bound — they are componentwise less stable, which is why they are rare in general-purpose libraries.

For a compatibility implementation the consequence is the same one recorded on MINVERSE: matching another implementation's bits means matching its op graph, not merely its algorithm. MMULT is the cleanest place in the family to see that, because there is nothing else going on.

If you need accuracy rather than compatibility

The remedies are well established and none of them is reachable from the worksheet:

  • Compensated (Kahan/Neumaier) summation, or the Dot2 algorithm of Ogita, Rump and Oishi ("Accurate sum and dot product", SIAM Journal on Scientific Computing 26(6), 2005), which delivers a result as accurate as one computed in twice the working precision, at a small constant multiple of the cost.
  • Extended-precision accumulation of the dot product before rounding once at the end.
  • Sorting or grouping terms by magnitude before summing, which helps in the mixed-sign case.

A natural-best implementation of MMULT should use a compensated dot product; an excel-bitexact one must not, unless Excel does. See implementation options for why those are different deliverables.

Overflow

Rust code

Show the CalcFunc source of this function

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.

MMULT · 6 lines · calc-func/src/catalogue/math.rs on GitHub
51 mmult "MMULT" (2, 2) [R "array1", R "array2"] |s, a, sv| {
    let left = matrix(s, a, 0, sv)?;
    let right = matrix(s, a, 1, sv)?;
    let product = k::matrix_family::mmult_kernel(&left, &right).map_err(Exit::Error)?;
    out_matrix(s, &product)
}
mmult_kernel · 32 lines · calc-func/src/ported/matrix_family.rs on GitHub
pub(crate) fn mmult_kernel(
    left: &[Vec<f64>],
    right: &[Vec<f64>],
) -> Result<Vec<Vec<f64>>, WorksheetErrorCode> {
    if left.is_empty() || right.is_empty() {
        return Err(WorksheetErrorCode::Value);
    }
    let left_cols = left.first().map_or(0, Vec::len);
    let right_cols = right.first().map_or(0, Vec::len);
    if left.iter().any(|row| row.len() != left_cols)
        || right.iter().any(|row| row.len() != right_cols)
    {
        return Err(WorksheetErrorCode::Value);
    }
    if left_cols != right.len() {
        return Err(WorksheetErrorCode::Value);
    }

    let mut result = Vec::with_capacity(left.len());
    for left_row in left {
        let mut row = Vec::with_capacity(right_cols);
        for col_idx in 0..right_cols {
            let mut acc = 0.0;
            for inner_idx in 0..left_cols {
                acc += left_row[inner_idx] * right[inner_idx][col_idx];
            }
            row.push(acc);
        }
        result.push(row);
    }
    Ok(result)
}

Kernels shared with other functions

Argument and result plumbing

No implementation has been published with a Handbook test suite for this function.

Version differences

No comparison between specific Excel builds is recorded here yet. This does not establish that all builds behave alike.

Alternatives

Relationships

  • MINVERSE — MMULT(A, MINVERSE(A)) against MUNIT is the residual test, and the most useful single thing to do with this family. Read the caution on the MINVERSE page first: a small residual is not a certificate of an accurate inverse.
  • MDETERM — det(AB) = det A · det B is a metamorphic identity requiring no oracle and exercising two surfaces at once.
  • MUNIT — the multiplicative identity; MMULT(A, MUNIT(n)) should return A unchanged, entry for entry, which is a strong exactness test because every dot product is a sum of exact zeros and one exact copy.
  • SUMPRODUCT — the single-dot-product function. MMULT of a row by a column is SUMPRODUCT, and the two are worth comparing: if they accumulate in different orders they will differ in the last bits for the same data, which is a clean, oracle-free way to see accumulation order made visible.
  • TRANSPOSE — the other shape operator, and the partner in (AB)ᵀ = BᵀAᵀ.
  • LINEST and the regression family — they form and solve normal equations, which are MMULT(TRANSPOSE(X), X) in disguise. That product is famously the step that squares the condition number, which is why forming normal equations explicitly is discouraged.

Named variants

No named variants are recorded yet.

Suggest a correction or contribute an implementation · For agents