Arithmetic and Comparison Operators
Most logic eventually comes down to math: counting, scaling, comparing. This page covers the operators you'll use, the built-in math functions, and the rounding rules you need to know when integers and decimals meet.
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.
Arithmetic Operators
| Operator | Description | Example |
|---|---|---|
| + | Addition | Total := A + B; |
| - | Subtraction | Delta := A - B; |
| * | Multiplication | Product := A * 5; |
| / | Division | Average := Sum / Count; |
| MOD | Remainder | Remainder := A MOD B; |
Use parentheses to control the order, just like in regular math:
Result := (Value1 + Value2) * 3;
Comparison Operators
Comparison operators ask a true/false question. They show up most often inside an IF.
| Operator | Description | Example |
|---|---|---|
= | Equal to | IF Mode = 1 THEN |
<> | Not equal to | IF State <> 0 THEN |
< | Less than | IF Temp < 100 THEN |
> | Greater than | IF Speed > Limit THEN |
<= | Less than or equal | IF Level <= 50 THEN |
>= | Greater or equal | IF Count >= Max THEN |
Division: integers vs decimals
Division is the operator that surprises people most often, because the result depends on what you're dividing into. The destination tag's type decides whether you keep the fraction or lose it.
Try this in your head: 7 / 2. The exact answer is 3.5. What you get in the tag depends on where you're putting it:
DintResult := 7 / 2; // DintResult = 3 — integer truncation
RealResult := 7 / 2; // RealResult = 3.5 — fraction preserved
DintResult := 7.0 / 2; // DintResult = 4 — see "Banker rounding" below
The full rule:
| Left operand | Right operand | Destination | What you get |
|---|---|---|---|
DINT | DINT | DINT | Integer division (truncates to zero) |
DINT | DINT | REAL | Float division (fraction preserved) |
any REAL | any | DINT | Float division, then banker rounding |
any REAL | any | REAL | Float division |
A small detail worth knowing: integer division always truncates toward zero, not down. So -7 / 2 is -3, not -4.
Banker rounding (REAL into DINT)
When you put a decimal value into a DINT, Studio uses banker rounding — also called round half to even.
You probably learned in school: round halves up (0.5 → 1, 1.5 → 2, 2.5 → 3, 3.5 → 4). Banker rounding does something slightly different — when the value is exactly halfway, it rounds to the nearest even number:
| Value | School rounding | Banker rounding |
|---|---|---|
0.5 | 1 | 0 |
1.5 | 2 | 2 |
2.5 | 3 | 2 |
3.5 | 4 | 4 |
-2.5 | -2 | -2 |
-3.5 | -3 | -4 |
For values that aren't exactly halfway, banker rounding works the same as normal rounding: 1.4 → 1, 1.6 → 2.
Why use it? Because rounding all halves up adds a small bias: if you round many measurements over and over, the total drifts a little high. Banker rounding splits halves evenly between rounding up and rounding down, so the bias cancels out. Industrial controllers and financial systems use it for the same reason.
DintSetpoint := RealSetpoint; // banker rounding applied automatically
Division by zero
Dividing by zero is normally an error. Studio handles it without crashing the program. The result depends on the operand types and the destination type:
| What you wrote | What you get |
|---|---|
DintDest := 5 / 0; | DintDest = 5 — Source A copied (DINT/DINT) |
DintDest := 0 / 0; | DintDest = 0 — Source A copied |
DintDest := 5.0 / 0.0; | DintDest = 2147483647 — +Infinity clamps to MAX_DINT |
DintDest := -5.0 / 0.0; | DintDest = -2147483648 — -Infinity clamps to MIN_DINT |
DintDest := 0.0 / 0.0; | DintDest = 0 — NaN becomes 0 |
RealDest := 5.0 / 0.0; | RealDest = +Infinity |
RealDest := 0.0 / 0.0; | RealDest = NaN ("Not a Number") |
For a DINT destination, all-DINT division copies the left operand (Source A); when any operand is REAL, the float result is clamped into the DINT range so you never see Infinity in an integer tag. A REAL destination keeps the raw +Infinity, -Infinity, or NaN value. The program keeps running in every case.
If you want to handle a possible zero divisor explicitly, check for it first:
IF Divisor <> 0 THEN
Result := Numerator / Divisor;
ELSE
Result := 0; // or whatever fallback makes sense
END_IF;
Built-in math functions
These are the math functions you can call from any expression:
| Function | What it does |
|---|---|
ABS(x) | Absolute value (drops the minus sign). |
SQRT(x) | Square root. Negative inputs become SQRT(ABS(x)). |
SIN(x) | Sine of x, where x is in radians. |
COS(x) | Cosine of x, in radians. |
TAN(x) | Tangent of x, in radians. |
DintDest := ABS(-7); // 7
RealDest := SQRT(-1.0); // 1.0 — never NaN, never an error
RealDest := SIN(0.0); // 0.0
There is no MIN or MAX function. Use IF/ELSE:
// "Smaller" = MIN(A, B)
IF A < B THEN
Smaller := A;
ELSE
Smaller := B;
END_IF;
Numeric literals
Numbers you type directly into your code are called literals.
| Form | Type | Example |
|---|---|---|
42 | DINT | Counter := 42; |
-42 | DINT | Offset := -42; |
3.14 | REAL | Pi := 3.14; |
1.0e3 | REAL | Scale := 1.0e3; |
2.5E-2 | REAL | Gain := 2.5E-2; |
Scientific notation must include a decimal point: write 1.0e3, not 1e3. The decimal point is what tells Studio you mean a REAL value.
Putting it together
// Add two values and scale the result
Result := (Value1 + Value2) * 3;
// Trigger an alarm if temperature exceeds 100
IF Temperature > 100 THEN
Alarm := 1;
END_IF;
// Convert raw ADC count (0-4095) into a percentage (0-100)
// REAL destination keeps the fraction — RawCount = 2047 gives ~49.988
ScaledReal := (RawCount * 100) / 4095;
// Same expression into a DINT — RawCount = 2047 gives 49 (truncated, not rounded)
DintScaled := (RawCount * 100) / 4095;
// Round a REAL setpoint to the nearest DINT (banker rounding applies)
DintSetpoint := RealSetpoint;