# Math — ADD / SUB / MUL / DIV / MOD

Source: https://rungs.dev/docs/ladder-logic/instructions/math

> Reference for Ladder Logic math instructions in rungs.dev — ADD, SUB, MUL, DIV, MOD — performing arithmetic on two sources and writing the result to a destination tag.

Perform arithmetic on two numeric sources and write the result to a destination tag. Math instructions act like coils — place them on the right side of a rung.

> **INFO**
>
> **Modeled on Studio 5000 Logix Designer**
>
> [rungs.dev](https://studio.rungs.dev) models its math behavior on Studio 5000 Logix Designer®
> — division, rounding, and type coercion follow the same conventions. Rare edge cases may
> differ.

## Available Math Instructions [#available-math-instructions]

| Instruction | Symbol                           | Purpose                                                                                                                              |
| ----------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `ADD`       |  | Writes sourceA + sourceB into the destination tag.                                                                                   |
| `SUB`       |  | Writes sourceA − sourceB into the destination tag.                                                                                   |
| `MUL`       |  | Writes sourceA × sourceB into the destination tag.                                                                                   |
| `DIV`       |  | Writes sourceA ÷ sourceB into the destination tag. Behavior depends on operand and destination types — see Division Semantics below. |
| `MOD`       |  | Writes the remainder of sourceA ÷ sourceB into the destination tag.                                                                  |

These five combine two sources. The one-input math instructions — `ABS`, `SQRT`, and
`NEG` — have [their own page](https://rungs.dev/docs/ladder-logic/instructions/abs-sqrt-neg).

## Operands [#operands]

| Name      | Type                                |
| --------- | ----------------------------------- |
| `sourceA` | `DINT` \| `INT` \| `SINT` \| `REAL` |
| `sourceB` | `DINT` \| `INT` \| `SINT` \| `REAL` |
| `dest`    | `DINT` \| `INT` \| `SINT` \| `REAL` |

## How It Works [#how-it-works]

When the rung is true, the instruction reads both sources, performs the operation, and writes the result to the destination — once per scan. If the rung stays true, the instruction runs every scan.

## Example — Running Total [#example--running-total]

```ladder
XIC(PartDetected)ONS(EdgeBit)ADD(TotalParts,1,TotalParts)
```

Each rising edge of `PartDetected` adds `1` to `TotalParts`.

## Example — Scale a Reading [#example--scale-a-reading]

Convert a raw ADC value (`0`–`4095`) into a percentage (`0`–`100`).

```ladder
MUL(RawValue,100,Scaled);DIV(Scaled,4095,Scaled)
```

If `Scaled` is a `DINT`, the division truncates toward zero — `RawValue = 2047` becomes `Scaled = 49`, not `49.99`. Declare `Scaled` as `REAL` if you need the fractional precision.

> **IDEA**
>
> **Practice**
>
> Try the math in the [Scale exercise](https://rungs.dev/exercises/scale) — convert a raw 4–20 mA signal into an engineering-unit temperature.

## How DIV decides what to give you [#how-div-decides-what-to-give-you]

`DIV` is the one math instruction whose answer depends on where you put it: the result comes from the types of the source values _and_ the destination tag.

The simplest example: divide `7` by `2`. Mathematically that's `3.5`. What `DIV` writes depends on where it's writing:

| Rung                    | Result | Why                                                                          |
| ----------------------- | ------ | ---------------------------------------------------------------------------- |
| `DIV(7, 2, DintDest)`   | `3`    | All integers, integer destination → truncate to zero                         |
| `DIV(7, 2, RealDest)`   | `3.5`  | REAL destination keeps the fraction                                          |
| `DIV(7.0, 2, DintDest)` | `4`    | A REAL operand makes it float division (3.5), then banker rounding ties even |
| `DIV(5.0, 2, DintDest)` | `2`    | Same — `2.5` rounds to even (`2`)                                            |
| `DIV(-7, 2, DintDest)`  | `-3`   | Integer division truncates **toward zero**, not down                         |

The pattern: if _both_ source operands are `DINT` _and_ the destination is `DINT`, you get integer division. Anywhere a `REAL` shows up — operand or destination — you get float math, possibly followed by banker rounding when the destination is still `DINT`.

See [Arithmetic and Comparison Operators](https://rungs.dev/docs/structured-text/arithmetic-and-comparison-operators) for the full table including banker rounding details.

## Division by zero [#division-by-zero]

Dividing by zero is normally an error. `DIV` handles it without halting the program. What gets written depends on the source operand types and the destination type:

| You wrote                  | Result                                                    |
| -------------------------- | --------------------------------------------------------- |
| `DIV(5, 0, DintDest)`      | `DintDest = 5` — Source A copied (DINT/DINT)              |
| `DIV(0, 0, DintDest)`      | `DintDest = 0` — Source A copied                          |
| `DIV(5.0, 0.0, DintDest)`  | `DintDest = 2147483647` — `+Infinity` clamps to MAX_DINT  |
| `DIV(-5.0, 0.0, DintDest)` | `DintDest = -2147483648` — `-Infinity` clamps to MIN_DINT |
| `DIV(0.0, 0.0, DintDest)`  | `DintDest = 0` — `NaN` becomes 0                          |
| `DIV(5.0, 0.0, RealDest)`  | `RealDest = +Infinity`                                    |
| `DIV(0.0, 0.0, RealDest)`  | `RealDest = NaN`                                          |
| `DIV(5, 0, RealDest)`      | `RealDest = +Infinity` — see the note below               |

For a `DINT` destination, all-DINT division copies the left operand (Source A); when any source is `REAL`, the float result is clamped into the DINT range so an integer tag never holds `Infinity`. A `REAL` destination keeps the raw `+Infinity`, `-Infinity`, or `NaN` value. The rung continues in every case in this table.

Watch the last row. Both sources are whole numbers, yet the answer is `+Infinity`. **The destination type decides the kind of maths**, not just the sources: `DIV(5, 0, DintDest)` is integer division and copies Source A to give `5`, while `DIV(5, 0, RealDest)` is float division and gives `+Infinity`. Only the destination tag changed.

### One place it does stop the program [#one-place-it-does-stop-the-program]

All of that is division in ordinary maths. A divide by zero **inside an array index** behaves differently — the controller stops the scan:

```ladder
MOVE(1,Buffer[Count/0])
```

There is no element for the index to reach, so nothing is written and the remaining rungs never run. Gate the index on a non-zero divisor the same way you would gate a `DIV`.

If you'd rather detect the zero explicitly and do something different, gate the `DIV` with a compare:

```ladder
NE(Divisor,0)DIV(Numerator,Divisor,Result)
```

## MOD — the remainder [#mod--the-remainder]

`MOD` writes what is left over after dividing Source A by Source B. Classic uses: split a
count into full boxes ("144 parts, 12 per box — how many don't fit?"), or make something
happen every Nth part.

```ladder
MOD(TotalParts,12,LeftOver)
```

| Rung                      | Result      | Why                                                              |
| ------------------------- | ----------- | ---------------------------------------------------------------- |
| `MOD(7, 3, DintDest)`     | `1`         | 7 = 2×3 + **1**                                                  |
| `MOD(-7, 3, DintDest)`    | `-1`        | the sign follows Source A (the dividend)                         |
| `MOD(7, -3, DintDest)`    | `1`         | same rule — Source B's sign is ignored                           |
| `MOD(7.5, 2.0, RealDest)` | `1.5`       | REAL operands keep the fraction                                  |
| `MOD(7, 0, DintDest)`     | `0`         | integer MOD by zero writes 0 — unlike DIV, which copies Source A |
| `MOD(7.5, 0.0, RealDest)` | `+Infinity` | REAL MOD by zero gives Infinity with the sign of Source A        |

## Common Mistakes [#common-mistakes]

- Running an `ADD` accumulator without an `ONS` — the value runs away as it adds every scan.
- Expecting `DINT / DINT` into a `DINT` destination to round — it truncates toward zero. Use a `REAL` destination, or a `REAL` operand, to keep the fraction.
- Assuming `DIV` by zero raises a fault — `DINT` destinations receive Source A, `REAL` destinations receive `Infinity`/`NaN`.
- Writing the operation the wrong way around — the destination is the **last** operand.

## Related [#related]

- [MOVE](https://rungs.dev/docs/ladder-logic/instructions/move) — copy a value without computing
- [Compare instructions](https://rungs.dev/docs/ladder-logic/instructions/compare) — act on the result of a calculation
- [One Shot](https://rungs.dev/docs/ladder-logic/instructions/one-shot-ons) — guard per-scan accumulation