Integer Operators
Let’s take a look at the operators for integers.
Comparison operators
const std = @import("std");
const expect = std.testing.expect;
test "comparison operators" {
try expect(1 == 1);
try expect(1 != 2);
try expect(1 >= 1 and 2 >= 1);
try expect(2 > 1);
try expect(1 <= 1 and 1 <= 2);
try expect(1 < 2);
}$ zig test comparison.zig
All 1 tests passed.
Simple arithmetic
const std = @import("std");
const expect = std.testing.expect;
test "+ - and *" {
try expect(1 + 2 == 3);
try expect(1 - 2 == -1);
try expect(2 * 3 == 6);
}$ zig test arithmetic.zig
All 1 tests passed.
Division
const std = @import("std");
const expect = std.testing.expect;
test "/ for non-negative integers" {
try expect(4 / 2 == 2);
try expect(3 / 2 == 1); // round down
try expect(2 / 2 == 1);
}
test "/ for negative integers, no remainder" {
try expect(-4 / 2 == -2);
try expect(4 / -2 == -2);
try expect(-4 / -2 == 2);
}$ zig test division.zig
All 2 tests passed.
The tricky case is when the result is negative and not a whole
number. Zig takes an unusal route here: unless the compiler can prove
this doesn’t happen, you can’t use the / operator.
Instead, there’s @divTrunc, @divFloor, and @divExact:
const std = @import("std");
const expect = std.testing.expect;
test "@divTrunc rounds towards zero" {
try expect(@divTrunc(3, 2) == 1);
try expect(@divTrunc(-3, 2) == -1);
}
test "@divFloor rounds down" {
try expect(@divFloor(3, 2) == 1);
try expect(@divFloor(-3, 2) == -2);
}
test "@divExact is only valid if there’s no remainder" {
try expect(@divExact(6, 2) == 3);
try expect(@divExact(-6, 2) == -3);
}$ zig test division-functions.zig
All 3 tests passed.
Assignment operators
+= and friends combine an operator with assignment:
const std = @import("std");
const expect = std.testing.expect;
test "+= -= *= /=" {
var a: u64 = 3;
a += 1;
try expect(a == 4);
a -= 2;
try expect(a == 2);
a *= 3;
try expect(a == 6);
a /= 2;
try expect(a == 3);
}$ zig test combined.zig
All 1 tests passed.
Wrapping and saturating operators
Overflow – getting a result outside the range of the data type – is illegal behavior with regular arithmetic operators, but there are wrapping and saturating operators that allow it:
const std = @import("std");
const expect = std.testing.expect;
test "wrapping operators" {
// Results wrap around to the type’s smallest value.
const a: i8 = 127;
const b: i8 = 2;
try expect(a +% b == -127);
try expect(a *% b == -2);
// Or the largest.
const c: u8 = 0;
const d: u8 = 2;
try expect(c -% d == 254);
}
test "saturating operators" {
// The result is the type’s largest value.
const a: i8 = 127;
const b: i8 = 2;
try expect(a +| b == 127);
try expect(a *| b == 127);
// Or the smallest.
const c: u8 = 0;
const d: u8 = 2;
try expect(c -| d == 0);
}$ zig test overflow.zig
All 2 tests passed.
Bitwise operators
const std = @import("std");
const expect = std.testing.expect;
test "bitwise operators" {
const a: u8 = 0b00001111;
const b: u8 = 0b00111100;
try expect(a & b == 0b00001100);
try expect(a | b == 0b00111111);
try expect(a ^ b == 0b00110011);
try expect(~a == 0b11110000);
try expect(b >> 2 == 0b00001111);
try expect(b << 2 == 0b11110000);
try expect(b << 4 == 0b11000000);
try expect(b <<| 4 == 0b11111111);
}$ zig test bitwise.zig
All 1 tests passed.
Next example: Floating-point numbers.