{
  "category": "Math and trigonometry functions",
  "display_name": "FACT",
  "entry_kind": "built_in_function",
  "eval_api": {
    "method": "POST",
    "url": "https://handbook.dnacalc.com/api/eval"
  },
  "function_id": "FUNC.FACT",
  "implementation_state": {
    "answer_bank": {
      "excel_builds": [
        "20430"
      ],
      "larger_numeric": 0,
      "last_bit": 0,
      "matched": 2709,
      "other_mismatch": 0,
      "refused": 0,
      "rows": 2709,
      "unread": 0,
      "worst_ulp": 0
    },
    "detail": "Recorded: 2,709 in the answer bank and 3 workbook cells.",
    "dnacalc_commit": "9e1884369a678158dd82d3d74188daf986995e87",
    "in_catalogue": true,
    "state": "matches",
    "state_label": "Matches every recorded Excel answer",
    "summary": "Matches all 2,712 recorded answers",
    "workbook_corpus": {
      "cells": 3,
      "matched": 3
    }
  },
  "implementations": [
    {
      "commit": "9e1884369a678158dd82d3d74188daf986995e87",
      "flavour": "excel-compatibility-candidate",
      "language": "Rust",
      "name": "CalcFunc",
      "own_code": [
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/math.rs#L117-L117",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/fact.rs#L5-L17",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/fact.rs#L19-L29"
      ],
      "repository": "https://github.com/DnaCalc/DnaCalc",
      "shared_code": [
        "catalogue/math::unary",
        "ported/factorial_common::trunc_nonnegative"
      ],
      "source": "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/math.rs#L117-L117",
      "state": "matches",
      "suite": null,
      "summary": "Matches all 2,712 recorded answers"
    }
  ],
  "json_url": "https://handbook.dnacalc.com/api/functions/FUNC.FACT.json",
  "name": "FACT",
  "notes": [],
  "schema": "efh.function-reference/v1",
  "section_order": [
    "what-it-does",
    "try-it",
    "quirks",
    "how-to-implement",
    "version-differences",
    "alternatives"
  ],
  "sections": {
    "alternatives": "\n### Relationships\n\n\n- **[FACTDOUBLE](FUNC.FACTDOUBLE.md)** — the skip-one variant `n·(n-2)·(n-4)·…`, related by\n  `n! = n!! · (n-1)!!`. That identity is a free cross-surface test and neither page's evidence\n  covers it.\n- **`GAMMA`** — the continuous extension: `GAMMA(n+1) = FACT(n)`. Excel exposes both, and they\n  must agree at the integers; whether they do bitwise is unchecked and is a strong probe.\n- **`GAMMALN`, `GAMMALN.PRECISE`** — the logarithm, which is what any implementation must use\n  above `n = 170` if it wants an answer at all. `LN(FACT(n))` and `GAMMALN(n+1)` are the two\n  routes to the same quantity and differ.\n- **[COMBIN](FUNC.COMBIN.md), [COMBINA](FUNC.COMBINA.md), `PERMUT`** — the combinatorial\n  surfaces whose documented formulas are written in factorials and whose implementations must\n  not be. See the numerical notes on [COMBIN](FUNC.COMBIN.md) for why.\n- **`MULTINOMIAL`** — the multinomial coefficient, a ratio of factorials with the same overflow\n  hazard.\n- **`SERIESSUM`, `EXP`** — Taylor-series machinery where factorials appear as denominators; in\n  that role the reciprocal `1/n!` is what is wanted, and it underflows gracefully where `n!`\n  overflows abruptly.\n- **`POISSON.DIST`, `BINOM.DIST`** — distributions with factorial kernels, which is why they\n  are implemented through log-gamma rather than through `FACT`.\n\n\n",
    "how-to-implement": "\n### Numerical notes\n\n\n`FACT` is the clearest case in this batch of a function where the **best implementation is a\ntable**, and where any cleverness is a mistake.\n\n**The argument.** The whole domain is `0 … 170`. That is 171 values. Storing 171 correctly\nrounded doubles costs about 1.4 kilobytes, gives a correctly-rounded answer for every input, is\nfaster than any loop, and removes the question of accumulated error entirely. Numerical Recipes'\n`factrl` does exactly this (with a smaller table and a log-gamma fallback), and there is no\nargument for anything else on a domain this size.\n\n**What a product loop costs.** Computing `n!` as a running product in binary64 rounds at every\nstep above the exactness boundary. The errors accumulate roughly as `√n` ulp in the best case\nand linearly in the worst, so by `n = 170` a naive loop can be several ulp from the correctly\nrounded value. It is never catastrophic and it is entirely avoidable. Note also that the loop's\n*order* matters: multiplying ascending and descending give different results, which is the same\nop-graph question that [COMBIN](FUNC.COMBIN.md)'s evidence turns on.\n\n**What the log-gamma route costs.** `exp(lnΓ(n+1))` extends past 170 in the sense that it does\nnot overflow internally, but the answer still does, and below 170 it is markedly less accurate\nthan either a table or a loop — the same amplification described on\n[COMBINA](FUNC.COMBINA.md), where a small relative error in a large logarithm becomes a large\nrelative error in the result. For `FACT` specifically there is no reason to pay it.\n\n**The one place the mathematics is genuinely subtle** is the interaction of truncation with the\ndomain check. `FACT(-0.5)` truncates to `0` and is therefore in the documented domain, while\n`-0.5` itself is not; `FACT(-1.5)` truncates to `-1`, which is not. An implementation that\nchecks the sign of the raw argument and one that checks the sign of the truncated argument\ndisagree on exactly the interval `(-1, 0)`. The documentation does not say which order applies.\n\n**For consumers**: when a formula needs a *ratio* of factorials, never form the factorials.\n`n!/(n-k)!` is a product of `k` terms; `n!/(k!(n-k)!)` is [COMBIN](FUNC.COMBIN.md). Both stay\nfinite far past where `FACT` stops, and both are more accurate. The presence of `FACT` on the\nworksheet is not a licence to write the textbook formula.\n\n\n",
    "quirks": "\n### Result and edge cases\n\n\nReturns `Number`, mathematically always a positive integer.\n\n- **`FACT(0)`** — 1, documented. The empty product convention, and the value that makes the\n  binomial coefficient formula work at its boundaries.\n- **Fractional arguments between 0 and 1** — truncate to 0, so the answer is 1.\n- **Negative arguments** — documented `#NUM!`. This is a genuine mathematical statement rather\n  than an implementation limit: `Γ` has poles at every non-positive integer, so `(-1)!` is\n  infinite, and the values between the poles alternate in sign with no combinatorial meaning.\n- **Fractional negative arguments** — truncate toward zero, so an argument in `(-1, 0)` becomes\n  `0` and would give 1 rather than an error. Whether Excel truncates before or after the\n  domain check is not documented, and it is the sharpest edge case on this page.\n- **Above the representable-exactly boundary** — the result is rounded, and *which* neighbour it\n  lands on is implementation-dependent. This is where a product loop and a table diverge.\n\n\n\n### Errors\n\n\nAs documented on Microsoft's `FACT` page:\n\n| Error | Condition |\n|---|---|\n| `#NUM!` | `number` is negative |\n\n| Error | Condition | Source |\n|---|---|---|\n| `#VALUE!` | Non-numeric argument | Shared call model; the page's worked examples imply it, and the sibling `FACTDOUBLE` page states it explicitly |\n| `#NUM!` | The true result is not finite | `EV-MATH-0008`, which names `FACT` — a kind and error-code convention, no count |\n\nThe split above is deliberate: only the first row is documented on the `FACT` page itself.\n\n\n",
    "sources": "- Handbook evidence record `EV-MATH-0008` — the FINITE error-code convention with named\n  witnesses including a `FACT` pair, and its explicit statement that it publishes no count. Read\n  its reader warning.\n- Abramowitz & Stegun, chapter 6 — the gamma function and the factorial identities; chapter 24\n  for the combinatorial context.\n- Press et al., *Numerical Recipes* — `factrl` and `gammln`.\n- Muller et al., *Handbook of Floating-Point Arithmetic* — exact representability of integers.\n- Handbook, [FACTDOUBLE](FUNC.FACTDOUBLE.md), [COMBIN](FUNC.COMBIN.md),\n  [COMBINA](FUNC.COMBINA.md);\n  [Coercion and lifting](../model/02-coercion-and-lifting.md).\n- Handbook projections `data/functions/FUNC.FACT.json` (the `non_finite=num` axis value) and\n  `data/presence/FUNC.FACT.json`.\n\n",
    "version-differences": "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.\n\n| Excel 16.0 build | Platform and CPU | Captured inputs |\n|---|---|---:|\n| 19929 | Windows, bitness not recorded, CPU not recorded | 24 |\n| 20430 | Windows, 64-bit, CPU not recorded | 2,709 |\n\n- XC-FACT-001: build 19929 versus build 20430; 3 same-input pairs, 0 differing answers. Compared axes: excel-build. Numeric answers were compared by their recorded binary64 bits; other answers by typed value.\n\nAgreement on these compared inputs does not establish agreement on unmeasured inputs.\n\n[Recorded environments and comparison details](https://github.com/DnaCalc/ExcelFunctionsHandbook/blob/5dc75391aa5125ccc5abdb095da85950980d6fd8/content/evidence/environments/FUNC.FACT.json).",
    "what-it-does": "\n`FACT(number)` returns the factorial. Microsoft's page states the definition as the product\n`1·2·3·…·number` and states that a non-integer argument is truncated.\n\n    n! = ∏_{i=1}^{n} i,    with 0! = 1\n\n| Property | Statement |\n|---|---|\n| Domain (documented) | non-negative; non-integers truncated |\n| Range | positive integers |\n| Recurrence | `n! = n · (n-1)!` |\n| Base case | `0! = 1` — the empty product |\n| Gamma relation | `n! = Γ(n+1)`, and `Γ` extends the factorial to all complex arguments except the non-positive integers |\n| Growth | Stirling: `n! ~ √(2πn) · (n/e)^n` |\n| Log form | `ln(n!) = lnΓ(n+1)`, the quantity `GAMMALN` supplies |\n| Exactly representable | for `n ≤ 22` only |\n| Largest finite | `170!`; `171!` exceeds the binary64 range |\n\nTwo boundaries govern this function and they are far apart. Above `n = 22` the true factorial is\nno longer a binary64 value, so every larger result is rounded. Above `n = 170` it is not even\nclose — the value exceeds the format's range entirely. The whole interesting domain of `FACT` is\ntherefore the 148 integers between them, which is small enough that an implementation can simply\n**tabulate every correctly-rounded answer**. Few functions in this Handbook have that property.\n\nThe reason `22` is the exactness limit is worth stating, because it is not obvious: `n!` has\n`n - s₂(n)` trailing zero bits, where `s₂(n)` is the population count of `n`. Exact\nrepresentability needs the bit-length of `n!` minus those trailing zeros to fit in 53 bits, and\n`23!` is the first factorial for which it does not.\n\n\n\n### Arguments\n\n\n| Argument | Meaning | Constraint (as documented) |\n|---|---|---|\n| `number` | The value whose factorial is wanted. Required. | non-negative; non-integers are truncated |\n\n"
  },
  "signature": {
    "display": "FACT(number)",
    "parameters": [
      {
        "description": null,
        "name": "number",
        "optional": false,
        "repeats": false
      }
    ],
    "trailing_repeats": false
  },
  "sources": {
    "metadata": {
      "admission": {
        "label": "implemented",
        "reason": null
      },
      "arity": {
        "max": 1,
        "min": 1
      },
      "category": "Math and trigonometry functions",
      "classification": {
        "arg_preparation_profile": "ValuesOnlyPreAdapter",
        "coercion_lift_profile": "UnaryNumericScalarOrArrayElementwise",
        "determinism_class": "Deterministic",
        "error_collapse_profile": "None",
        "fec_dependency_profile": "None",
        "host_interaction_class": "None",
        "kernel_signature_class": "Custom",
        "rich_value_usage": "RichBlind",
        "surface_fec_dependency_profile": "RefOnly",
        "thread_safety_class": "SafePure",
        "volatility_class": "NonVolatile"
      },
      "descriptions": {
        "long": null,
        "short": "Factorial: the product 1 x 2 x ... x n",
        "short_source": "dna-calc-own"
      },
      "docs": {
        "english_description": "Returns the factorial of a number",
        "microsoft_url": "https://support.microsoft.com/en-us/office/fact-function-ca8588c2-15f2-41c0-8e8c-c11bd471a4f3"
      },
      "entry_kind": "built_in_function",
      "localized_family_names": null,
      "localized_names": [
        {
          "locale": "ar-SA",
          "name": "FACT"
        },
        {
          "locale": "bg-BG",
          "name": "FACT"
        },
        {
          "locale": "cs-CZ",
          "name": "FAKTORIÁL"
        },
        {
          "locale": "da-DK",
          "name": "FAKULTET"
        },
        {
          "locale": "de-DE",
          "name": "FACT"
        },
        {
          "locale": "el-GR",
          "name": "FACT"
        },
        {
          "locale": "en-AU",
          "name": "FACT"
        },
        {
          "locale": "en-GB",
          "name": "FACT"
        },
        {
          "locale": "en-US",
          "name": "FACT"
        },
        {
          "locale": "es-ES",
          "name": "FACT"
        },
        {
          "locale": "et-EE",
          "name": "FACT"
        },
        {
          "locale": "fi-FI",
          "name": "KERTOMA"
        },
        {
          "locale": "fr-FR",
          "name": "FACT*"
        },
        {
          "locale": "he-IL",
          "name": "FACT"
        },
        {
          "locale": "hr-HR",
          "name": "FACT"
        },
        {
          "locale": "hu-HU",
          "name": "FAKT"
        },
        {
          "locale": "id-ID",
          "name": "FACT"
        },
        {
          "locale": "it-IT",
          "name": "FACT"
        },
        {
          "locale": "ja-JP",
          "name": "FACT"
        },
        {
          "locale": "ko-KR",
          "name": "FACT"
        },
        {
          "locale": "lt-LT",
          "name": "FACT"
        },
        {
          "locale": "lv-LV",
          "name": "FACT"
        },
        {
          "locale": "nb-NO",
          "name": "OPPLYSNING"
        },
        {
          "locale": "nl-NL",
          "name": "FACULTEIT"
        },
        {
          "locale": "pl-PL",
          "name": "SILNIA"
        },
        {
          "locale": "pt-BR",
          "name": "FACT"
        },
        {
          "locale": "pt-PT",
          "name": "FACTORIAL"
        },
        {
          "locale": "ro-RO",
          "name": "FACT"
        },
        {
          "locale": "ru-RU",
          "name": "ФАКТР"
        },
        {
          "locale": "sk-SK",
          "name": "FACT"
        },
        {
          "locale": "sl-SI",
          "name": "FACT"
        },
        {
          "locale": "sr-Latn-RS",
          "name": "FACT"
        },
        {
          "locale": "sv-SE",
          "name": "FAKULTET"
        },
        {
          "locale": "th-TH",
          "name": "FACT"
        },
        {
          "locale": "tr-TR",
          "name": "ÇARPINIM işlevi"
        },
        {
          "locale": "uk-UA",
          "name": "FACT"
        },
        {
          "locale": "vi-VN",
          "name": "FACT"
        },
        {
          "locale": "zh-CN",
          "name": "FACT"
        },
        {
          "locale": "zh-HK",
          "name": "FACT"
        },
        {
          "locale": "zh-TW",
          "name": "FACT"
        }
      ],
      "signature": {
        "display": "FACT(number)",
        "parameters": [
          {
            "description": null,
            "name": "number",
            "optional": false,
            "repeats": false
          }
        ],
        "trailing_repeats": false
      },
      "surface_name": "FACT",
      "version_marker": null,
      "xlcall": {
        "code": "184",
        "symbol": "xlfFact"
      }
    },
    "microsoft": "https://support.microsoft.com/en-us/office/fact-function-ca8588c2-15f2-41c0-8e8c-c11bd471a4f3"
  },
  "try_it": {
    "formula": "=FACT(0)"
  },
  "url": "https://handbook.dnacalc.com/functions/FACT.html",
  "variants": [],
  "versions": [
    {
      "behaviour": "24 captured inputs are indexed for this environment. Capture dates are not recorded. This is a reading count, not a comparison with another build.",
      "excel_build": "16.0 build 19929",
      "platform": "Windows, bitness not recorded, CPU not recorded",
      "sources": [
        "https://github.com/DnaCalc/ExcelFunctionsHandbook/blob/5dc75391aa5125ccc5abdb095da85950980d6fd8/content/evidence/environments/FUNC.FACT.json"
      ]
    },
    {
      "behaviour": "2,709 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.",
      "excel_build": "16.0 build 20430",
      "platform": "Windows, 64-bit, CPU not recorded",
      "sources": [
        "https://github.com/DnaCalc/ExcelFunctionsHandbook/blob/5dc75391aa5125ccc5abdb095da85950980d6fd8/content/evidence/environments/FUNC.FACT.json"
      ]
    }
  ]
}