Arithmetic

The arithmetic operators compute with integers and floating-point numbers.

OperatorOperationExampleLevel
-negation (prefix)-a16
*multiplicationa * b14
/divisiona / b14
%remaindera % b14
+additiona + b13
-subtractiona - b13
let a: int32 = 17;
let b: int32 = 5;
PrintLine("{} {} {} {} {} {}", a + b, a - b, a * b, a / b, a % b, -a);

This prints 22 12 85 3 2 -17. Multiplication, division and remainder bind more tightly than addition and subtraction, and each level groups from the left — see Precedence.

Operand types

Both operands must agree on a type, following the operand rules: one type, a literal that takes the other operand's type, or two integers of one signedness, where the narrower widens. The result has that type.

An unsuffixed integer literal adapts only to another integer. Beside a floating-point value, write a floating-point literal:

let half = 1.5 + 2;
error: operator '+' cannot combine left operand 'float64' with right operand 'int'

Write 1.5 + 2.0. A bool is not a number, and flag + flag is error: operator '+' cannot combine left operand 'bool8' with right operand 'bool8'. To compute with a character's code, convert it with as first, as in (c as int32) - ('0' as int32).

Integer overflow wraps

Integer +, -, * and unary - wrap: a result that does not fit the type keeps its low bits, the two's-complement result, in every build profile.

var top: int8 = int8::Max;
top += 1;
let small: uint8 = 0;
let m: uint8 = 200;
PrintLine("{} {} {}", top, small - 1, m * 2);

This prints -128 255 144. Negating an unsigned value wraps the same way. When overflow must be noticed instead, Core's AddChecked, SubChecked and MulChecked report it — see the Learn lessons Wrapping arithmetic and Checked arithmetic.

A literal is not allowed to wrap on its way in: let narrow: uint8 = 0; narrow + 300 is error: integer literal is out of range for type 'uint8'.

Integer division

Integer / truncates towards zero, and % gives the remainder that goes with it, so it has the sign of the dividend:

ExpressionValue
7 / 23
-7 / 2-3
7 % 21
-7 % 2-1
7 % -21

Dividing by zero is never allowed to produce a value. Integer / and %, and the compound /= and %=, stop the program when the divisor is zero, on every target and in every build profile:

func Divide(a: int32, b: int32) -> int32 {
    return a / b;
}
Panic: division by zero
  at Divide (Src/Main.rux:4:14)

The same happens to the one signed division whose quotient does not fit its type — the type's minimum divided by -1 — which stops with Panic: division overflow. A divisor written as a non-zero literal needs no check, and a release build drops a check it can prove will pass. The panic is the same kind a call to Panic produces — see Panics.

Floating-point arithmetic

float32 and float64 arithmetic follows IEEE 754. Division by zero does not stop the program; it gives an infinity, or NaN for 0.0 / 0.0:

PrintLine("{} {}", 1.0 / 0.0, 0.0 / 0.0);
PrintLine("{} {}", 7.5 % 2.0, -7.5 / 2.0);

This prints Inf NaN, then 1.5 -3.75. % on floating-point values is the remainder of truncated division, with the sign of the dividend. See Floating-point types for the special values and their comparisons.

No power operator

Rux has no exponentiation operator. a ** b is two operators, multiplication and a dereference, a * (*b), so with integers it fails:

error: operator '*' requires a pointer operand, but found 'int'

Use Pow from the Math package for floating-point powers, or a loop for small integer ones.

There is no unary + either: +5 does not parse.

Compound assignment

Each binary arithmetic operator has a compound form, += -= *= /= %=, that updates a mutable place, and ++ and -- add or subtract one — see Assignment.

See also