{
  "category": "Math and trigonometry functions",
  "display_name": "ROMAN",
  "entry_kind": "built_in_function",
  "eval_api": {
    "method": "POST",
    "url": "https://handbook.dnacalc.com/api/eval"
  },
  "function_id": "FUNC.ROMAN",
  "implementation_state": {
    "answer_bank": null,
    "detail": "Recorded: 4 workbook cells.",
    "dnacalc_commit": "9e1884369a678158dd82d3d74188daf986995e87",
    "in_catalogue": true,
    "state": "matches",
    "state_label": "Matches every recorded Excel answer",
    "summary": "Matches all 4 recorded answers",
    "workbook_corpus": {
      "cells": 4,
      "matched": 4
    }
  },
  "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#L167-L181",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/roman_fn.rs#L8-L14",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/roman_fn.rs#L16-L22",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/roman_fn.rs#L24-L30",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/roman_fn.rs#L32-L40",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/roman_fn.rs#L42-L64",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/roman_fn.rs#L66-L88",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/roman_fn.rs#L90-L114",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/roman_fn.rs#L116-L142",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/roman_fn.rs#L144-L156",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/roman_fn.rs#L158-L168"
      ],
      "repository": "https://github.com/DnaCalc/DnaCalc",
      "shared_code": [],
      "source": "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/math.rs#L167-L181",
      "state": "matches",
      "suite": null,
      "summary": "Matches all 4 recorded answers"
    }
  ],
  "json_url": "https://handbook.dnacalc.com/api/functions/FUNC.ROMAN.json",
  "name": "ROMAN",
  "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- **`ARABIC`** — the inverse direction, parsing a Roman numeral back to a number. The pair is not\n  a clean bijection: `ARABIC` must accept the concise forms that `ROMAN` can emit, and it accepts\n  many strings `ROMAN` would never produce. Round-tripping `ROMAN` then `ARABIC` is the natural\n  consistency test and is on the probe list below.\n- **`TEXT`** — the general number-to-string formatter, which has no Roman format code. `ROMAN` is\n  the only route.\n- **`BASE` and `DECIMAL`** — the other pair of numeral-system converters, for radix 2–36. They are\n  the structural analogue of `ROMAN`/`ARABIC` for positional systems, and the contrast is\n  instructive: `BASE` is total on its domain and mechanical, while `ROMAN` needs a five-way style\n  argument because the target notation is not positional and admits genuine ambiguity.\n- **Confused with `ROUND`** by autocomplete alone. They share four letters and nothing else.\n\n\n",
    "how-to-implement": "\n### Notes for implementers\n\n\n**The place-value decomposition is the whole algorithm for form 0**, and it should be written as\nfour table lookups rather than a greedy subtraction loop. The greedy loop happens to produce the\nsame answer for form 0 and cannot express forms 1–4 at all, because those introduce contractions\nthat cross place boundaries (`VL` spans tens and units; `LD` spans hundreds and tens).\n\n**Forms are ranges, not points.** The concise contractions apply to *bands* — 45–49, 95–99,\n450–499, 490–499, 495–499, 499 — each of which is rendered as a prefix plus the standard rendering\nof the remainder. Getting a band boundary wrong produces a wrong answer on exactly one or two\ninputs out of four thousand, which no casual test will find. The full domain is only 4000 × 5\ninputs; an implementation should simply enumerate all 20,000 and compare.\n\n**The thousands prefix is form-independent.** All five forms render the thousands digit as\n`\"\"`/`M`/`MM`/`MMM` and apply their contractions only to the remainder below 1000. That is what\nmakes `IM` for 999 possible while `MIM` for 1999 comes out as `M` + `IM`.\n\n**Preserve the value kind.** The result is text. An implementation that returns a number-shaped\nvalue, or that lets the empty-string case become an empty cell, changes downstream behaviour — see\nthe raw-versus-published boundary in [The value universe](../model/01-value-universe.md).\n\n\n",
    "quirks": "\n### The five forms\n\n- **Form 1** adds `VL` for 45–49 and `VC` for 95–99 in the tens/units, and `LD` for 450–499,\n  `LM` for 950–999 in the hundreds.\n- **Form 2** adds `IL` for 49 and `IC` for 99, and `XD` for 490–499, `XM` for 990–999.\n- **Form 3** adds `VD` for 495–499 and `VM` for 995–999.\n- **Form 4** adds `ID` for 499 and `IM` for 999.\n\nThe right way to read the ladder is as a nesting: `ROMAN(n, k)` is never longer than\n`ROMAN(n, k−1)`, and the forms differ only on numbers close to 50, 100, 500 and 1000 from below.\nFor every other input all five forms agree.\n\n\n\n### Result and edge cases\n\n\nReturns `Text` — a string, not a number. This is the only surface in this batch whose result kind\nis not `Number`, and it matters downstream: the result does not participate in arithmetic without\ncoercion, and it sorts as text.\n\n- **Non-integral `form`** is likewise truncated.\n- **Text length.** The longest result is `MMMDCCCLXXXVIII` for 3888 in form 0 — fifteen\n  characters, comfortably inside every text limit.\n\n\n",
    "sources": "- Microsoft, \"WorksheetFunction.Roman method (Excel)\" —\n  <https://learn.microsoft.com/en-us/office/vba/api/excel.worksheetfunction.roman> (the form table\n  quoted above, and the two `#VALUE!` conditions; the page does not distinguish forms 1, 2 and 3\n  from one another and does not mention zero or non-integral input).\n- Microsoft, Excel JavaScript API reference —\n  <https://learn.microsoft.com/en-us/javascript/api/excel/excel.functions> (`roman` returns a\n  string; `form` accepts number, string or boolean).\n- Handbook evidence record `EV-STRUCT-0007`, rendered beside this page with its own reader\n  warning.\n- Handbook, [The value universe](../model/01-value-universe.md),\n  [Coercion and lifting](../model/02-coercion-and-lifting.md).\n- Handbook projections `data/functions/FUNC.ROMAN.json` (arity 1–2, `Custom` coercion,\n  `Deterministic`) and `data/presence/FUNC.ROMAN.json` (own module; defect stream\n  `BUG-FUNC-028`).\n\n",
    "version-differences": "No comparison between specific Excel builds is recorded here yet. This does not establish that all builds behave alike.",
    "what-it-does": "\n`ROMAN(number, [form])` converts an Arabic numeral to a Roman numeral, returned as **text**.\n\nThe classical system is additive with a subtractive shorthand: the letters `I V X L C D M` have\nvalues 1, 5, 10, 50, 100, 500, 1000, and a smaller letter placed before a larger one is subtracted.\nStandard rendering decomposes the number by decimal place and emits the canonical group for each:\n\n| Place | Groups |\n|---|---|\n| thousands | `\"\"`, `M`, `MM`, `MMM` |\n| hundreds | `\"\"`, `C`, `CC`, `CCC`, `CD`, `D`, `DC`, `DCC`, `DCCC`, `CM` |\n| tens | `\"\"`, `X`, `XX`, `XXX`, `XL`, `L`, `LX`, `LXX`, `LXXX`, `XC` |\n| units | `\"\"`, `I`, `II`, `III`, `IV`, `V`, `VI`, `VII`, `VIII`, `IX` |\n\nThe domain ceiling of 3999 follows directly: the thousands row runs out at `MMM`, and the classical\nsystem has no letter for 5000 — the overbar notation for larger values is not available in plain\ntext. The floor is 0, which the classical system also has no letter for.\n\n`form` selects how aggressively the rendering may abbreviate beyond the canonical groups. That is\nwhere the function stops being mathematics and becomes a table of conventions — see below.\n\n\n\n### Arguments\n\n\n| Argument | Meaning | Default |\n|---|---|---|\n| `number` | The Arabic numeral to convert. Required. | — |\n| `form` | How concise the Roman numeral should be. Optional. | 0 (classic) |\n\nMicrosoft's VBA reference documents `form` as follows, verbatim:\n\n| Form | Type |\n|---|---|\n| 0 or omitted | Classic. |\n| 1 | More concise. |\n| 2 | More concise. |\n| 3 | More concise. |\n| 4 | Simplified. |\n| TRUE | Classic. |\n| FALSE | Simplified. |\n\n**What the documentation does not say is the whole difficulty.** Forms 1, 2 and 3 are all\ndescribed as \"More concise\", with no statement of how they differ, no examples, and no rule. A\nreader cannot determine from Microsoft's documentation what `ROMAN(499, 2)` should be. The only\nway to know is to observe it.\n\n\n"
  },
  "signature": {
    "display": "ROMAN(number, [form])",
    "parameters": [
      {
        "description": null,
        "name": "number",
        "optional": false,
        "repeats": false
      },
      {
        "description": null,
        "name": "form",
        "optional": true,
        "repeats": false
      }
    ],
    "trailing_repeats": false
  },
  "sources": {
    "metadata": {
      "admission": {
        "label": "implemented",
        "reason": null
      },
      "arity": {
        "max": 2,
        "min": 1
      },
      "category": "Math and trigonometry functions",
      "classification": {
        "arg_preparation_profile": "ValuesOnlyPreAdapter",
        "coercion_lift_profile": "Custom",
        "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": "A number written as a Roman numeral, in one of several styles",
        "short_source": "dna-calc-own"
      },
      "docs": {
        "english_description": "Converts an Arabic numeral to Roman, as text",
        "microsoft_url": "https://support.microsoft.com/en-us/office/roman-function-d6b0b99e-de46-4704-a518-b45a0f8b56f5"
      },
      "entry_kind": "built_in_function",
      "localized_family_names": null,
      "localized_names": [
        {
          "locale": "ar-SA",
          "name": "ROMAN"
        },
        {
          "locale": "bg-BG",
          "name": "ROMAN"
        },
        {
          "locale": "cs-CZ",
          "name": "ROMAN"
        },
        {
          "locale": "da-DK",
          "name": "ROMERTAL"
        },
        {
          "locale": "de-DE",
          "name": "ROMAN"
        },
        {
          "locale": "el-GR",
          "name": "ROMAN"
        },
        {
          "locale": "en-AU",
          "name": "ROMAN"
        },
        {
          "locale": "en-GB",
          "name": "ROMAN"
        },
        {
          "locale": "en-US",
          "name": "ROMAN"
        },
        {
          "locale": "es-ES",
          "name": "NUMERO.ROMANO"
        },
        {
          "locale": "et-EE",
          "name": "ROMAN"
        },
        {
          "locale": "fi-FI",
          "name": "ROMAN"
        },
        {
          "locale": "fr-FR",
          "name": "ROMAIN*"
        },
        {
          "locale": "he-IL",
          "name": "ROMAN"
        },
        {
          "locale": "hr-HR",
          "name": "ROMAN"
        },
        {
          "locale": "hu-HU",
          "name": "RÓMAI"
        },
        {
          "locale": "id-ID",
          "name": "ROMAN"
        },
        {
          "locale": "it-IT",
          "name": "ROMAN"
        },
        {
          "locale": "ja-JP",
          "name": "ROMAN"
        },
        {
          "locale": "ko-KR",
          "name": "ROMAN"
        },
        {
          "locale": "lt-LT",
          "name": "ROMAN"
        },
        {
          "locale": "lv-LV",
          "name": "ROMAN"
        },
        {
          "locale": "nb-NO",
          "name": "ROMAN"
        },
        {
          "locale": "nl-NL",
          "name": "ROMEINS"
        },
        {
          "locale": "pl-PL",
          "name": "RZYMSKIE"
        },
        {
          "locale": "pt-BR",
          "name": "ROMAN"
        },
        {
          "locale": "pt-PT",
          "name": "ROMANO"
        },
        {
          "locale": "ro-RO",
          "name": "ROMAN"
        },
        {
          "locale": "ru-RU",
          "name": "РИМСКОЕ"
        },
        {
          "locale": "sk-SK",
          "name": "ROMAN"
        },
        {
          "locale": "sl-SI",
          "name": "ROMAN"
        },
        {
          "locale": "sr-Latn-RS",
          "name": "ROMAN"
        },
        {
          "locale": "sv-SE",
          "name": "ROMERSK"
        },
        {
          "locale": "th-TH",
          "name": "ROMAN"
        },
        {
          "locale": "tr-TR",
          "name": "ROMEN"
        },
        {
          "locale": "uk-UA",
          "name": "ROMAN"
        },
        {
          "locale": "vi-VN",
          "name": "ROMAN"
        },
        {
          "locale": "zh-CN",
          "name": "ROMAN"
        },
        {
          "locale": "zh-HK",
          "name": "ROMAN"
        },
        {
          "locale": "zh-TW",
          "name": "ROMAN"
        }
      ],
      "signature": {
        "display": "ROMAN(number, [form])",
        "parameters": [
          {
            "description": null,
            "name": "number",
            "optional": false,
            "repeats": false
          },
          {
            "description": null,
            "name": "form",
            "optional": true,
            "repeats": false
          }
        ],
        "trailing_repeats": false
      },
      "surface_name": "ROMAN",
      "version_marker": null,
      "xlcall": {
        "code": "354",
        "symbol": "xlfRoman"
      }
    },
    "microsoft": "https://support.microsoft.com/en-us/office/roman-function-d6b0b99e-de46-4704-a518-b45a0f8b56f5"
  },
  "try_it": {
    "formula": "=ROMAN(2026)"
  },
  "url": "https://handbook.dnacalc.com/functions/ROMAN.html",
  "variants": [],
  "versions": []
}