Como Usar Operações Matemáticas em Zig

Introdução

Zig oferece um módulo matemático completo em std.math com funções para aritmética avançada, trigonometria, logaritmos e constantes. Diferente de muitas linguagens, Zig trata overflow e operações inválidas como erros, proporcionando mais segurança em cálculos numéricos.

Nesta receita, você aprenderá a usar as principais funções matemáticas disponíveis.

Pré-requisitos

Funções Básicas

const std = @import("std");
const math = std.math;

pub fn main() !void {
    // Valor absoluto
    std.debug.print("abs(-42) = {d}\n", .{@abs(@as(i32, -42))});
    std.debug.print("abs(-3.14) = {d:.2}\n", .{@abs(@as(f64, -3.14))});

    // Mínimo e máximo
    std.debug.print("max(10, 20) = {d}\n", .{@max(@as(i32, 10), @as(i32, 20))});
    std.debug.print("min(10, 20) = {d}\n", .{@min(@as(i32, 10), @as(i32, 20))});

    // Clamp (limitar a um intervalo)
    std.debug.print("clamp(15, 0, 10) = {d}\n", .{math.clamp(@as(i32, 15), 0, 10)});
    std.debug.print("clamp(-5, 0, 10) = {d}\n", .{math.clamp(@as(i32, -5), 0, 10)});
    std.debug.print("clamp(5, 0, 10) = {d}\n", .{math.clamp(@as(i32, 5), 0, 10)});
}

Raiz Quadrada e Potências

const std = @import("std");
const math = std.math;

pub fn main() !void {
    // Raiz quadrada
    std.debug.print("sqrt(16) = {d:.1}\n", .{math.sqrt(@as(f64, 16.0))});
    std.debug.print("sqrt(2) = {d:.6}\n", .{math.sqrt(@as(f64, 2.0))});

    // Potência
    std.debug.print("pow(2, 10) = {d:.0}\n", .{math.pow(f64, 2.0, 10.0)});
    std.debug.print("pow(3, 3) = {d:.0}\n", .{math.pow(f64, 3.0, 3.0)});

    // Raiz cúbica
    std.debug.print("cbrt(27) = {d:.1}\n", .{math.cbrt(@as(f64, 27.0))});

    // Exponencial (e^x)
    std.debug.print("exp(1) = {d:.6}\n", .{math.exp(@as(f64, 1.0))});
    std.debug.print("exp(2) = {d:.6}\n", .{math.exp(@as(f64, 2.0))});
}

Trigonometria

const std = @import("std");
const math = std.math;

pub fn main() !void {
    const pi = math.pi;

    std.debug.print("Constante pi = {d:.10}\n", .{pi});

    // Graus para radianos
    const graus: f64 = 45.0;
    const radianos = graus * pi / 180.0;
    std.debug.print("\n{d:.0}° = {d:.6} rad\n", .{ graus, radianos });

    // Funções trigonométricas
    std.debug.print("\nsin(45°) = {d:.6}\n", .{@sin(radianos)});
    std.debug.print("cos(45°) = {d:.6}\n", .{@cos(radianos)});
    std.debug.print("tan(45°) = {d:.6}\n", .{@tan(radianos)});

    // Funções inversas
    const valor: f64 = 0.5;
    std.debug.print("\nasin({d}) = {d:.6} rad\n", .{ valor, math.asin(valor) });
    std.debug.print("acos({d}) = {d:.6} rad\n", .{ valor, math.acos(valor) });
    std.debug.print("atan({d}) = {d:.6} rad\n", .{ valor, math.atan(valor) });

    // atan2 (ângulo de um ponto)
    std.debug.print("atan2(1, 1) = {d:.6} rad ({d:.1}°)\n", .{
        math.atan2(@as(f64, 1.0), @as(f64, 1.0)),
        math.atan2(@as(f64, 1.0), @as(f64, 1.0)) * 180.0 / pi,
    });
}

Logaritmos

const std = @import("std");
const math = std.math;

pub fn main() !void {
    // Logaritmo natural (base e)
    std.debug.print("ln(1) = {d:.6}\n", .{@log(@as(f64, 1.0))});
    std.debug.print("ln(e) = {d:.6}\n", .{@log(@as(f64, math.e))});
    std.debug.print("ln(10) = {d:.6}\n", .{@log(@as(f64, 10.0))});

    // Logaritmo base 2
    std.debug.print("\nlog2(8) = {d:.6}\n", .{math.log2(@as(f64, 8.0))});
    std.debug.print("log2(1024) = {d:.6}\n", .{math.log2(@as(f64, 1024.0))});

    // Logaritmo base 10
    std.debug.print("\nlog10(100) = {d:.6}\n", .{math.log10(@as(f64, 100.0))});
    std.debug.print("log10(1000) = {d:.6}\n", .{math.log10(@as(f64, 1000.0))});

    // Constantes
    std.debug.print("\ne = {d:.10}\n", .{math.e});
    std.debug.print("pi = {d:.10}\n", .{math.pi});
    std.debug.print("ln(2) = {d:.10}\n", .{math.ln2});
}

Arredondamento

const std = @import("std");
const math = std.math;

pub fn main() !void {
    const valores = [_]f64{ 3.2, 3.5, 3.7, -2.3, -2.5, -2.7 };

    std.debug.print("{s:>8} {s:>8} {s:>8} {s:>8}\n", .{ "Valor", "Floor", "Ceil", "Round" });
    std.debug.print("{s:>8} {s:>8} {s:>8} {s:>8}\n", .{ "-----", "-----", "-----", "-----" });

    for (valores) |v| {
        std.debug.print("{d:>8.1} {d:>8.1} {d:>8.1} {d:>8.1}\n", .{
            v,
            @floor(v),
            @ceil(v),
            @round(v),
        });
    }
}

Saída esperada

   Valor    Floor     Ceil    Round
   -----    -----    -----    -----
     3.2      3.0      4.0      3.0
     3.5      3.0      4.0      4.0
     3.7      3.0      4.0      4.0
    -2.3     -3.0     -2.0     -2.0
    -2.5     -3.0     -2.0     -2.0
    -2.7     -3.0     -2.0     -3.0

Exemplo Prático: Distância entre Coordenadas

const std = @import("std");
const math = std.math;

const Coordenada = struct {
    lat: f64, // graus
    lon: f64, // graus
};

fn distanciaHaversine(a: Coordenada, b: Coordenada) f64 {
    const R = 6371.0; // Raio da Terra em km
    const to_rad = math.pi / 180.0;

    const dlat = (b.lat - a.lat) * to_rad;
    const dlon = (b.lon - a.lon) * to_rad;
    const lat1 = a.lat * to_rad;
    const lat2 = b.lat * to_rad;

    const sa = @sin(dlat / 2.0);
    const sb = @sin(dlon / 2.0);
    const h = sa * sa + @cos(lat1) * @cos(lat2) * sb * sb;

    return 2.0 * R * math.asin(math.sqrt(h));
}

pub fn main() !void {
    const sao_paulo = Coordenada{ .lat = -23.5505, .lon = -46.6333 };
    const rio = Coordenada{ .lat = -22.9068, .lon = -43.1729 };
    const brasilia = Coordenada{ .lat = -15.7975, .lon = -47.8919 };

    std.debug.print("SP -> RJ: {d:.1} km\n", .{distanciaHaversine(sao_paulo, rio)});
    std.debug.print("SP -> BSB: {d:.1} km\n", .{distanciaHaversine(sao_paulo, brasilia)});
    std.debug.print("RJ -> BSB: {d:.1} km\n", .{distanciaHaversine(rio, brasilia)});
}

Dicas e Boas Práticas

  1. Use @as para especificar tipos: Funções de std.math precisam saber o tipo do argumento.

  2. Builtins vs std.math: @sqrt, @sin, @cos, @log, @floor, @ceil, @round são builtins otimizados.

  3. Cuidado com NaN e Inf: Operações inválidas (como sqrt(-1)) podem retornar NaN. Verifique com math.isNan().

  4. Precisão de float: f64 tem ~15 dígitos significativos, f32 tem ~7.

Receitas Relacionadas

Tutoriais Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.