# Expressions in Ladder

Source: https://rungs.dev/docs/ladder-logic/expressions

> How to write an expression inside a ladder box with CPT and CMP — operator order, the REAL promotion rule, and the operators that do not behave the way maths does.

Most ladder instructions take tags: `ADD(Speed, Offset, Result)`. Two do not. `CPT` and `CMP`
take a whole **expression** — a piece of maths — in one box, so you can write

```
CPT(Result, (Speed * 60) / PulsesPerRev)
```

instead of chaining a `MUL` box into a `DIV` box through a scratch tag.

- **`CPT`** (Compute) works out the expression and stores the answer in a tag. It is an output
  instruction, and it runs on **every scan the rung is true**, not once per rising edge.
- **`CMP`** (Compare) works out the expression and uses the answer as the rung condition. It is
  an input instruction, so it sits on the left of the rung like a contact.

This page is about the rules expressions follow, because a few of them are not what you
would guess.

## What you can put in an expression [#what-you-can-put-in-an-expression]

|                                                                                            | `CPT` | `CMP` |
| ------------------------------------------------------------------------------------------ | ----- | ----- |
| `+` `-` `*` `/` `MOD`, brackets, negative sign                                             | ✓     | ✓     |
| `**` (raise to a power)                                                                    | ✓     | ✓     |
| `AND` `OR` `XOR` `NOT` — **bitwise**, see below                                            | ✓     | ✓     |
| `ABS` `SQRT` `TRUNC` `LN` `LOG` `DEG` `RAD` `SIN` `COS` `TAN` `ASIN` `ACOS` `ATAN` `ATAN2` | ✓     | ✓     |
| `<` `<=` `>` `>=` `=` `<>` — comparisons                                                   | —     | ✓     |
| `&&` `\|\|` `^^` `!` — **logical**, see below                                              | —     | ✓     |
| `IsINF` `IsNAN` — test a REAL for infinity or "not a number"                               | —     | ✓     |

`CPT` has no comparisons because a comparison is not a number you can store. `CMP` has
everything, because its answer is a yes/no.

## Operator order [#operator-order]

Brackets first, then down this list. Operators on the same line run left to right.

| Order | Operators                                                                                                                           |
| ----- | ----------------------------------------------------------------------------------------------------------------------------------- |
| 1     | `( )`                                                                                                                               |
| 2     | functions: `ABS` `SQRT` `TRUNC` `LN` `LOG` `DEG` `RAD` `SIN` `COS` `TAN` `ASIN` `ACOS` `ATAN` `ATAN2`, and `IsINF` `IsNAN` in `CMP` |
| 3     | `**`                                                                                                                                |
| 4     | `-` (negative sign), `NOT`, `!`                                                                                                     |
| 5     | `*` `/` `MOD`                                                                                                                       |
| 6     | `+` `-`                                                                                                                             |
| 7     | `AND`                                                                                                                               |
| 8     | `XOR`                                                                                                                               |
| 9     | `OR`                                                                                                                                |
| 10    | `<` `<=` `>` `>=` `=` `<>`                                                                                                          |
| 11    | `&&`                                                                                                                                |
| 12    | `^^`                                                                                                                                |
| 13    | `\|\|`                                                                                                                              |

Three rows of that table are traps. Here they are.

### `-2**2` is `-4`, not `4` [#-22-is--4-not-4]

`**` is on row 3 and the negative sign is on row 4, so the power runs **first** and the minus
sign is applied to the answer.

| You write | It means      | Answer |
| --------- | ------------- | ------ |
| `-2**2`   | `-(2**2)`     | `-4`   |
| `(-2)**2` | `(-2) * (-2)` | `4`    |

If you want the sign to be part of the number being squared, put brackets round it.

### `2**3**2` is `64`, not `512` [#232-is-64-not-512]

Two `**` in a row run **left to right**, like `-` and `/` do. Maths and most programming
languages go right to left here, so this is the one place a ladder expression disagrees with
them.

| You write   | It means             | Answer |
| ----------- | -------------------- | ------ |
| `2**3**2`   | `(2**3)**2` = `8**2` | `64`   |
| `2**(3**2)` | `2**9`               | `512`  |

### `AND` is bitwise, `&&` means "both" [#and-is-bitwise--means-both]

`AND`, `OR`, `XOR` and `NOT` work on the **bits inside a number**, one bit at a time. They do
not mean "both of these are true".

With `Count = 2` (binary `10`) and the number `1` (binary `01`):

| Expression               | What happens                        | Answer                      |
| ------------------------ | ----------------------------------- | --------------------------- |
| `Count AND 1`            | `10` AND `01` — no bit is 1 in both | `0`, so the rung is **off** |
| `(Count > 0) && (1 > 0)` | both comparisons are true           | rung is **on**              |

To join two comparisons you must use `&&` ("both"), `\|\|` ("either") or `^^` ("exactly one"),
and to flip a comparison you use `!`. Writing `Count > 1 AND Limit < 5` in a ladder box is an
error, not a slow way of getting the right answer — Studio will tell you to use `&&`.

This is the one rule that differs from Structured Text, where `AND` between two comparisons
_does_ mean "both". Inside a ladder box, keep `AND` for bits and `&&` for conditions.

There is a knock-on worth knowing if you write both. In Structured Text, that logical `AND`
also splits the decimal decision below in two, so each side decides for itself. In a ladder
box, `&&` splits nothing — one decimal anywhere still reaches everything.

## `CMP` is true for any value that is not zero [#cmp-is-true-for-any-value-that-is-not-zero]

`CMP` does not need a comparison in it. It takes whatever number the expression produces and
treats **any non-zero value as true** — negative numbers included.

| `CMP(…)`                       | Rung                     |
| ------------------------------ | ------------------------ |
| `CMP(5)`                       | on                       |
| `CMP(0)`                       | off                      |
| `CMP(-1)`                      | on                       |
| `CMP(Count)` with `Count = -1` | on                       |
| `CMP(2.5 - 2.5)`               | off — the answer is zero |

So `CMP(Count)` asks "is Count anything other than zero?", which is often what you want and
occasionally not what you meant.

## Whole numbers and decimals in the same expression [#whole-numbers-and-decimals-in-the-same-expression]

Studio has two kinds of number: `DINT` (a whole number) and `REAL` (a number with a decimal
part). Which one an expression uses changes the answer, because dividing two whole numbers throws
the fraction away.

The rule is decided **once, for the whole expression, before anything is worked out**. Every step
of the expression uses decimals if any one of these is true:

- any value in it is a `REAL` — a tag, or a number written with a decimal point like `1.5`
- it calls `SIN` `COS` `TAN` `ASIN` `ACOS` `ATAN` `LN` `LOG` `DEG` or `RAD`
- the tag you are storing into is a `REAL`

Otherwise every step stays whole.

That list of functions is exact. `SQRT`, `TRUNC` and `ABS` are **not** on it, even though the
first two hand back a decimal — `7/2 + SQRT(4)` is `5`, because nothing in it is decimal.

"Whole expression" really does mean the whole thing. It reaches through brackets, through a
comparison, and in and out of a function's brackets: in `TRUNC(10/4*4) * 1.5` the `1.5` at the
end changes how `10/4` inside the brackets is worked out.

| Formula   | Stored in | Worked out as                                         | Answer |
| --------- | --------- | ----------------------------------------------------- | ------ |
| `7/2`     | `DINT`    | whole: `7/2` = `3`                                    | `3`    |
| `7/2`     | `REAL`    | decimal, because the destination is `REAL`            | `3.5`  |
| `10/4*4`  | `DINT`    | whole: `10/4` = `2`, then `× 4`                       | `8`    |
| `7/2*1.5` | `DINT`    | decimal, because `1.5` is there: `3.5 × 1.5` = `5.25` | `5`    |

That last row is the one to remember. The `1.5` sits at the _end_ of the expression, but it still
changes how `7/2` at the start is worked out. One decimal anywhere makes the whole expression
decimal.

Storing a decimal answer into a `DINT` rounds it — and `5.25` rounds down to `5`.

## Storing into a whole number rounds to even [#storing-into-a-whole-number-rounds-to-even]

When a decimal answer lands in a `DINT`, `INT` or `SINT` tag it is rounded to the nearest whole
number. Exact halves round to the nearest **even** number, so `0.5` becomes `0` and `1.5`
becomes `2`. This is the same rounding `MOVE` and `ADD` use, and it stops a long run of halves
drifting upwards.

## Dividing by zero [#dividing-by-zero]

Dividing by zero in an expression does **not** stop the controller. The answer is the number
you were dividing — `7 / 0` gives `7` — and the scan carries on.

The exception is dividing by zero inside square brackets, where you are working out which
element of an array to use. There is no sensible element number to fall back on, so that does
stop the scan. Check the divisor before you use it as an index.

## Decimals are stored to about 7 digits [#decimals-are-stored-to-about-7-digits]

A `REAL` keeps roughly seven significant digits, so some answers are very slightly off and a
few surprising things come out true:

- `0.1 + 0.2 = 0.3` is **true**, because both sides land on the same stored value
- `LN(2.718281828)` gives `0.99999994`, not exactly `1`

Never test two decimals for exact equality if either one came out of a calculation. Compare
against a range instead — `ABS(Measured - Target) < 0.01`.

## Where to go next [#where-to-go-next]

- [`CPT` — Compute](https://rungs.dev/docs/ladder-logic/instructions/cpt)
- [`CMP` — Compare](https://rungs.dev/docs/ladder-logic/instructions/cmp)
- [Arithmetic and comparison operators in Structured Text](https://rungs.dev/docs/structured-text/arithmetic-and-comparison-operators)