Vectors
A Vector is a group of numbers, booleans, or pointers that are operated on in parallel. They’re implemented with SIMD instructions where possible.
Floating-point vectors
const std = @import("std");
const print = std.debug.print;
pub fn main() void {
// defining vectors
const a: @Vector(4, f64) = .{ 1, -1, 2, -2 };
print("a = {}\n", .{a});
const b: @Vector(4, f64) = @splat(-2);
print("b = {}\n", .{b});
print("\n", .{});
// accessing elements
print("a[0] = {}\n", .{a[0]});
print("a[1] = {}\n", .{a[1]});
print("a[2] = {}\n", .{a[2]});
print("a[3] = {}\n", .{a[3]});
print("\n", .{});
// element-wise arithmetic with vectors
print("a+b = {}\n", .{a + b});
print("a-b = {}\n", .{a - b});
print("a*b = {}\n", .{a * b});
print("a/b = {}\n", .{a / b});
print("\n", .{});
// using builtin functions
const pi = std.math.pi;
const c: @Vector(4, f64) = .{
0, 0.5 * pi, pi, 1.5 * pi,
};
print("c = {d:.2}\n", .{c});
print("sin(c) = {d:.2}\n", .{@sin(c)});
print("cos(c) = {d:.2}\n", .{@cos(c)});
}$ zig run vectors.zig
a = { 1, -1, 2, -2 }
b = { -2, -2, -2, -2 }
a[0] = 1
a[1] = -1
a[2] = 2
a[3] = -2
a+b = { -1, -3, 0, -4 }
a-b = { 3, 1, 4, 0 }
a*b = { -2, 2, -4, 4 }
a/b = { -0.5, 0.5, -1, 1 }
c = { 0.00, 1.57, 3.14, 4.71 }
sin(c) = { 0.00, 1.00, 0.00, -1.00 }
cos(c) = { 1.00, 0.00, -1.00, -0.00 }
Integer vectors
const std = @import("std");
const print = std.debug.print;
pub fn main() void {
var a: @Vector(3, i64) = .{ 1, 2, 3 };
print("a = {}\n", .{a});
const twos: @Vector(3, i64) = @splat(2);
// += and friends also work with vectors
print("plus 2:\n", .{});
a += twos;
print("a = {}\n", .{a});
// so do the builtin integer division functions
print("divided by 2, rounding down:\n", .{});
a = @divFloor(a, twos);
print("a = {}\n", .{a});
print("\n", .{});
// @reduce combines the elements of a vector
print("sum of a: {}\n", .{@reduce(.Add, a)});
print("product of a: {}\n", .{@reduce(.Mul, a)});
print("minimum of a: {}\n", .{@reduce(.Min, a)});
print("maximum of a: {}\n", .{@reduce(.Max, a)});
}$ zig run vectors-2.zig
a = { 1, 2, 3 }
plus 2:
a = { 3, 4, 5 }
divided by 2, rounding down:
a = { 1, 2, 2 }
sum of a: 5
product of a: 4
minimum of a: 1
maximum of a: 2
Boolean vectors
const std = @import("std");
const print = std.debug.print;
pub fn main() void {
// initialize a boolean vector
const a: @Vector(3, bool) = .{ true, false, true };
print("a = {}\n", .{a});
// create a boolean vector by comparing two vectors
const b: @Vector(3, bool) =
@Vector(3, f64){ 1, 2, -1 } >
@Vector(3, f64){ 0, 0, 0 };
print("b = {}\n", .{b});
print("\n", .{});
// basic boolean operations
print("not a: {}\n", .{!a});
print("a and b: {}\n", .{a & b});
print("a or b: {}\n", .{a | b});
print("\n", .{});
// use @reduce with booleans
print("all true in a? {}\n", .{@reduce(.And, a)});
print("any true in a? {}\n", .{@reduce(.Or, a)});
}$ zig run vectors-3.zig
a = { true, false, true }
b = { true, true, false }
not a: { false, true, false }
a and b: { true, false, false }
a or b: { true, true, true }
all true in a? false
any true in a? true
Converting to and from arrays
const std = @import("std");
const expectEqual = std.testing.expectEqual;
test "convert an array to a vector" {
const array = [_]i64{ 1, 2, 3, 4 };
const v1: @Vector(4, i64) = array;
try expectEqual(@Vector(4, i64){ 1, 2, 3, 4 }, v1);
}
test "convert a slice to a vector" {
const slice: []const i64 = &.{ 1, 2, 3, 4 };
const v2: @Vector(4, i64) = slice.*;
try expectEqual(@Vector(4, i64){ 1, 2, 3, 4 }, v2);
}
test "convert a vector to an array" {
const vector: @Vector(4, i64) = .{ 1, 2, 3, 4 };
const a: [4]i64 = vector;
try expectEqual([_]i64{ 1, 2, 3, 4 }, a);
}$ zig test vectors-4.zig
1/3 vectors-4.test.convert an array to a vector...OK
2/3 vectors-4.test.convert a slice to a vector...OK
3/3 vectors-4.test.convert a vector to an array...OK
All 3 tests passed.
Select and shuffle
@select selects elements
from two vectors based on a predicate vector, where
true selects an element from the first vector,
false from the second:
const std = @import("std");
const print = std.debug.print;
pub fn main() void {
const a: @Vector(4, f64) = .{ 1, 2, 3, 4 };
const b: @Vector(4, f64) = .{ 10, 20, 30, 40 };
const pred = [_]bool{ true, false, true, false };
const c = @select(f64, pred, a, b);
print("c = {}\n", .{c});
}$ zig run vectors-5.zig
c = { 1, 20, 3, 40 }
@shuffle selects elements from two vectors based on an integer vector called a “mask”. Positive mask elements select from the first vector, negative ones from the second:
const std = @import("std");
const print = std.debug.print;
pub fn main() void {
const a: @Vector(4, f64) = .{ 1, 2, 3, 4 };
const b: @Vector(4, f64) = .{ 10, 20, 30, 40 };
const mask = [_]i64{ 0, -1, 1, -2, 2, -3, 3, -4 };
const c = @shuffle(f64, a, b, mask);
print("c = {}\n", .{c});
}$ zig run vectors-6.zig
c = { 1, 10, 2, 20, 3, 30, 4, 40 }
This is the last example for now.