{
  "category": "Operators",
  "display_name": "Addition (+)",
  "entry_kind": "built_in_operator",
  "eval_api": {
    "method": "POST",
    "url": "https://handbook.dnacalc.com/api/eval"
  },
  "function_id": "FUNC.OP_ADD",
  "implementation_state": {
    "answer_bank": null,
    "detail": "Of 1,447 recorded answers (1,447 workbook cells), 3 do not match: 3 differ in kind or value.",
    "dnacalc_commit": "9e1884369a678158dd82d3d74188daf986995e87",
    "in_catalogue": true,
    "state": "differs",
    "state_label": "Known larger differences",
    "summary": "Known larger differences on 3 of 1,447 recorded answers",
    "workbook_corpus": {
      "cells": 1454,
      "matched": 1444,
      "other_mismatch": 3,
      "pending": 7,
      "worst_ulp": 0
    }
  },
  "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/arithmetic.rs#L38-L38",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/arithmetic.rs#L49-L54"
      ],
      "repository": "https://github.com/DnaCalc/DnaCalc",
      "shared_code": [
        "catalogue/arithmetic::binary",
        "catalogue/arithmetic::operands"
      ],
      "source": "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/arithmetic.rs#L38-L38",
      "state": "differs",
      "suite": null,
      "summary": "Known larger differences on 3 of 1,447 recorded answers"
    }
  ],
  "json_url": "https://handbook.dnacalc.com/api/functions/FUNC.OP_ADD.json",
  "name": "OP_ADD",
  "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- [`FUNC.OP_SUBTRACT`](FUNC.OP_SUBTRACT.md), [`FUNC.OP_MULTIPLY`](FUNC.OP_MULTIPLY.md),\n  [`FUNC.OP_DIVIDE`](FUNC.OP_DIVIDE.md), [`FUNC.OP_POWER`](FUNC.OP_POWER.md) — the other\n  binary arithmetic operators. They share the coercion story and differ in domain and error\n  set.\n- [`FUNC.OP_UNARY_PLUS`](FUNC.OP_UNARY_PLUS.md) — same character, different function.\n- `SUM` — the aggregate. `A1+A2` and `SUM(A1:A2)` are not interchangeable: `SUM` scans a\n  range under the ignore-text-and-empty policy, while `+` coerces its operands directly.\n  With `\"3\"` stored as text in `A1`, that difference is visible in the result.\n- `IMSUM` — complex addition over text-encoded complex numbers; unrelated machinery.\n\n\n",
    "how-to-implement": "\n### Notes for implementers\n\n\n- Implement `+` as a two-operand kernel over already-coerced doubles and put every\n  interesting decision in the coercion and broadcast layers, where it is shared and testable\n  once. That is the shape the reference implementation uses, and it is why the operator's own\n  module is small.\n- Do not reassociate. A compiler or expression optimizer that rewrites `(a+b)+c` into\n  `a+(b+c)` changes results in the last bits and breaks any comparison against Excel.\n- Broadcast is not `zip`. Row-vs-column operands produce a two-dimensional grid, not a\n  diagonal, and missing coordinates are `#N/A` values inside the result array, not a\n  whole-call failure.\n- `+` is declared `SafePure`, `Deterministic` and `NonVolatile`, with no host interaction, so\n  it is safe to evaluate off the calculation thread.\n\n\n",
    "quirks": "\n### Result and edge cases\n\n- **Text operands.** Governed by the shared to-number rule, not by anything specific to `+`.\n  The locale-dependent part of Excel's text-to-number recognizer (thousands separators,\n  currency symbols, date text) is flagged as an open area in chapter 02 and is not settled\n  here.\n- **Logical operands.** Convert to 1 and 0 by the shared rule.\n- **Empty and omitted.** Chapter 02 keeps `Empty` (a blank cell) and `Missing` (an omitted\n  argument slot) distinct at the call boundary, and states that what a function *does* with\n  each is per-family policy. This Handbook has not recorded `+`'s policy for a blank operand\n  against Excel. It is the first thing to probe.\n\n\n",
    "sources": "- `data/presence/FUNC.OP_ADD.json` — implementing module\n  `earlier-implementation/op_add.rs`, giving this page its family slug.\n- Handbook `content/model/01-value-universe.md`, `02-coercion-and-lifting.md`,\n  `03-call-pipeline.md` — the value kinds, to-number rules, Empty/Missing distinction,\n  broadcast rule, and the \"operators are functions\" model this page relies on.\n\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`A + B` converts each operand to a number and returns their IEEE-754 binary64 sum, rounded\nonce to nearest with ties to even.\n\nThe arithmetic is the easy half. The interesting half is everything the sentence assumes:\n\n2. **The conversion is per-operand and can fail.** Each operand goes through the shared\n   to-number rules of [coercion and lifting](../model/02-coercion-and-lifting.md): a number is\n   itself; a logical is 1 or 0; text that reads as a number parses; text that does not is a\n   named coercion failure; an error value propagates rather than converting.\n3. **The result is one rounded double, not an exact sum.** `+` is therefore not associative:\n   `(a+b)+c` and `a+(b+c)` are different functions of the same three numbers, and a workbook\n   that reassociates a chain of `+` can change its last bits.\n\n`+` is a two-argument function in this model, not a variadic one. `SUM` is a different\nfunction with a different coercion policy, not `+` folded over a list — the direct-argument\nversus range-scan asymmetry described in chapter 02 is exactly where the two part company.\n\n\n\n### Arguments\n\n\n| Position | Name | Meaning |\n|---|---|---|\n| 0 | `A` | Left addend. Required. |\n| 1 | `B` | Right addend. Required. |\n\nArity is exactly 2 (`Arity { min: 2, max: 2 }`); there are no optional arguments and no\ndefaults. `+` is commutative on numbers up to the sign of a zero result, so operand order is\nnot semantically load-bearing the way it is for `-`, `/` and `^` — but it is still two\npositions, and an array-shaped left operand and an array-shaped right operand broadcast\nagainst each other by the rules below.\n\nThe typed surface has a second `+` that is not this function: prefix `+x` is\n[`FUNC.OP_UNARY_PLUS`](FUNC.OP_UNARY_PLUS.md), a one-argument operator with materially\ndifferent operand handling. Confusing the two is the most common misreading of this row.\n\n\n"
  },
  "signature": {
    "display": "number1 + number2",
    "parameters": [
      {
        "description": null,
        "name": "number1",
        "optional": false,
        "repeats": false
      },
      {
        "description": null,
        "name": "number2",
        "optional": false,
        "repeats": false
      }
    ],
    "trailing_repeats": false
  },
  "sources": {
    "metadata": {
      "admission": {
        "label": "implemented",
        "reason": null
      },
      "arity": {
        "max": 2,
        "min": 2
      },
      "category": "Operators",
      "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": "NumsToNum",
        "rich_value_usage": "RichBlind",
        "surface_fec_dependency_profile": "RefOnly",
        "thread_safety_class": "SafePure",
        "volatility_class": "NonVolatile"
      },
      "descriptions": {
        "long": null,
        "short": null,
        "short_source": "no-short-description"
      },
      "docs": null,
      "entry_kind": "built_in_operator",
      "localized_family_names": null,
      "localized_names": [],
      "signature": {
        "display": "number1 + number2",
        "parameters": [
          {
            "description": null,
            "name": "number1",
            "optional": false,
            "repeats": false
          },
          {
            "description": null,
            "name": "number2",
            "optional": false,
            "repeats": false
          }
        ],
        "trailing_repeats": false
      },
      "surface_name": "OP_ADD",
      "version_marker": null,
      "xlcall": null
    },
    "microsoft": null
  },
  "try_it": {
    "formula": "=1+2"
  },
  "url": "https://handbook.dnacalc.com/functions/OP_ADD.html",
  "variants": [],
  "versions": []
}