Bibliotecas Matemáticas e Científicas em Zig
O Zig é uma linguagem excepcionalmente adequada para computação científica e matemática de alto desempenho. Com suporte nativo a operações SIMD, vetores de tamanho fixo avaliados em comptime e controle preciso de representação numérica, o Zig permite cálculos que rivalizam com Fortran e C em performance, mantendo legibilidade e segurança superiores.
std.math — A Base Matemática
A biblioteca padrão oferece um módulo matemático abrangente:
const std = @import("std");
const math = std.math;
pub fn main() void {
// Constantes
const pi = math.pi;
const e = math.e;
const inf = math.inf(f64);
_ = inf;
// Funções trigonométricas
const angulo: f64 = pi / 4.0;
const seno = @sin(angulo);
const cosseno = @cos(angulo);
const tangente = @tan(angulo);
_ = seno;
_ = cosseno;
_ = tangente;
// Exponencial e logaritmo
const exp_val = @exp(@as(f64, 1.0));
const ln_val = @log(@as(f64, e));
const log2_val = math.log2(@as(f64, 8.0)); // 3.0
const log10_val = math.log10(@as(f64, 1000.0)); // 3.0
_ = exp_val;
_ = ln_val;
_ = log2_val;
_ = log10_val;
// Potência e raiz
const potencia = math.pow(f64, 2.0, 10.0); // 1024
const raiz = @sqrt(@as(f64, 144.0)); // 12
const cbrt = math.cbrt(@as(f64, 27.0)); // 3
_ = potencia;
_ = raiz;
_ = cbrt;
// Arredondamento
const floor = @floor(@as(f64, 3.7)); // 3
const ceil = @ceil(@as(f64, 3.2)); // 4
const round = @round(@as(f64, 3.5)); // 4
_ = floor;
_ = ceil;
_ = round;
// Overflow seguro
const a: u32 = 4_000_000_000;
const b: u32 = 1_000_000_000;
const resultado = math.add(u32, a, b) catch {
std.debug.print("Overflow detectado!\n", .{});
return;
};
_ = resultado;
}
Álgebra Linear
zlm — Zig Linear Math
Biblioteca focada em operações com vetores e matrizes para gráficos e física:
const zlm = @import("zlm");
pub fn main() void {
// Vetores
const v1 = zlm.Vec3.new(1.0, 2.0, 3.0);
const v2 = zlm.Vec3.new(4.0, 5.0, 6.0);
const soma = v1.add(v2);
const produto_escalar = v1.dot(v2);
const produto_vetorial = v1.cross(v2);
const magnitude = v1.length();
const normalizado = v1.normalize();
_ = soma;
_ = produto_escalar;
_ = produto_vetorial;
_ = magnitude;
_ = normalizado;
// Matrizes 4x4
const identidade = zlm.Mat4.identity();
const translacao = zlm.Mat4.translation(zlm.Vec3.new(1, 2, 3));
const rotacao = zlm.Mat4.rotation(std.math.pi / 4.0, zlm.Vec3.new(0, 1, 0));
const escala = zlm.Mat4.scaling(zlm.Vec3.new(2, 2, 2));
// Composição de transformações
const modelo = identidade.mul(translacao).mul(rotacao).mul(escala);
// Transformar ponto
const ponto = zlm.Vec4.new(1, 0, 0, 1);
const transformado = modelo.mulVec(ponto);
_ = transformado;
}
Matrizes Genéricas
Implementação de matrizes NxM com operações completas:
fn Matrix(comptime T: type, comptime rows: usize, comptime cols: usize) type {
return struct {
data: [rows][cols]T,
const Self = @This();
pub fn zeros() Self {
return .{ .data = std.mem.zeroes([rows][cols]T) };
}
pub fn identity() Self {
comptime {
if (rows != cols) @compileError("Identidade requer matriz quadrada");
}
var m = zeros();
inline for (0..rows) |i| {
m.data[i][i] = 1;
}
return m;
}
pub fn mul(self: Self, comptime other_cols: usize, other: Matrix(T, cols, other_cols)) Matrix(T, rows, other_cols) {
var result = Matrix(T, rows, other_cols).zeros();
for (0..rows) |i| {
for (0..other_cols) |j| {
var soma: T = 0;
for (0..cols) |k| {
soma += self.data[i][k] * other.data[k][j];
}
result.data[i][j] = soma;
}
}
return result;
}
pub fn transpor(self: Self) Matrix(T, cols, rows) {
var result = Matrix(T, cols, rows).zeros();
for (0..rows) |i| {
for (0..cols) |j| {
result.data[j][i] = self.data[i][j];
}
}
return result;
}
pub fn determinante(self: Self) T {
comptime {
if (rows != cols) @compileError("Determinante requer matriz quadrada");
}
if (rows == 2) {
return self.data[0][0] * self.data[1][1] - self.data[0][1] * self.data[1][0];
}
// Expansão de Laplace para NxN
var det: T = 0;
for (0..cols) |j| {
const sinal: T = if (j % 2 == 0) 1 else -1;
det += sinal * self.data[0][j] * self.cofator(0, j);
}
return det;
}
};
}
SIMD — Computação Vetorial
O Zig tem suporte nativo a operações SIMD:
const std = @import("std");
pub fn somaVetoresSimd(a: []const f32, b: []const f32, resultado: []f32) void {
const vec_size = 8; // AVX: 8 floats por operação
var i: usize = 0;
while (i + vec_size <= a.len) : (i += vec_size) {
const va: @Vector(vec_size, f32) = a[i..][0..vec_size].*;
const vb: @Vector(vec_size, f32) = b[i..][0..vec_size].*;
resultado[i..][0..vec_size].* = va + vb;
}
// Processar elementos restantes
while (i < a.len) : (i += 1) {
resultado[i] = a[i] + b[i];
}
}
pub fn produtoEscalarSimd(a: []const f32, b: []const f32) f32 {
const vec_size = 8;
var soma_vec: @Vector(vec_size, f32) = @splat(0);
var i: usize = 0;
while (i + vec_size <= a.len) : (i += vec_size) {
const va: @Vector(vec_size, f32) = a[i..][0..vec_size].*;
const vb: @Vector(vec_size, f32) = b[i..][0..vec_size].*;
soma_vec += va * vb;
}
var soma: f32 = @reduce(.Add, soma_vec);
while (i < a.len) : (i += 1) {
soma += a[i] * b[i];
}
return soma;
}
Estatística
fn Estatistica(comptime T: type) type {
return struct {
pub fn media(dados: []const T) T {
var soma: T = 0;
for (dados) |v| soma += v;
return soma / @as(T, @floatFromInt(dados.len));
}
pub fn variancia(dados: []const T) T {
const m = media(dados);
var soma_quadrados: T = 0;
for (dados) |v| {
const diff = v - m;
soma_quadrados += diff * diff;
}
return soma_quadrados / @as(T, @floatFromInt(dados.len));
}
pub fn desvioPadrao(dados: []const T) T {
return @sqrt(variancia(dados));
}
pub fn mediana(dados: []T) T {
std.sort.pdq(T, dados, {}, std.sort.asc(T));
const n = dados.len;
if (n % 2 == 0) {
return (dados[n / 2 - 1] + dados[n / 2]) / 2.0;
}
return dados[n / 2];
}
};
}
Comptime para Constantes Matemáticas
Uma das vantagens únicas do Zig é o cálculo em tempo de compilação:
// Tabela de senos pré-calculada em comptime
const TABELA_SENOS = blk: {
var tabela: [360]f32 = undefined;
for (0..360) |i| {
const angulo = @as(f32, @floatFromInt(i)) * std.math.pi / 180.0;
tabela[i] = @sin(angulo);
}
break :blk tabela;
};
// Números primos em comptime
fn ehPrimo(n: u64) bool {
if (n < 2) return false;
if (n == 2) return true;
if (n % 2 == 0) return false;
var i: u64 = 3;
while (i * i <= n) : (i += 2) {
if (n % i == 0) return false;
}
return true;
}
const PRIMEIROS_PRIMOS = blk: {
var primos: [100]u64 = undefined;
var count: usize = 0;
var n: u64 = 2;
while (count < 100) : (n += 1) {
if (ehPrimo(n)) {
primos[count] = n;
count += 1;
}
}
break :blk primos;
};
Boas Práticas
- Use SIMD quando possível: Ganhos de 4-8x em operações vetoriais
- Pré-calcule em comptime: Tabelas de lookup eliminam cálculos em runtime
- Escolha precisão adequada: f32 para gráficos, f64 para ciência
- Monitore overflow: Use
math.addemath.mulpara operações seguras - Profile operações numéricas: Use as ferramentas de profiling
Próximos Passos
Explore as bibliotecas gráficas que usam álgebra linear extensivamente, o Mach Engine para simulações, e as bibliotecas de áudio para DSP. Consulte nossos tutoriais para projetos de computação científica.