@max em Zig — Referência e Exemplos

@max em Zig

O @max retorna o maior de dois valores. Funciona com inteiros, floats e valores comptime. Se os dois valores forem conhecidos em comptime, o resultado também é comptime. Complementar ao @min, é usado para garantir valores mínimos, limitar intervalos e encontrar extremos.

Sintaxe

@max(a: T, b: T) T

Parâmetros

  • a (T): Primeiro valor.
  • b (T): Segundo valor (mesmo tipo ou coercível).

Valor de retorno

Retorna T — o maior dos dois valores.

Exemplos práticos

Exemplo 1: Uso básico

const std = @import("std");

pub fn main() void {
    std.debug.print("max(10, 20) = {}\n", .{@max(@as(u32, 10), @as(u32, 20))}); // 20
    std.debug.print("max(-5, 3) = {}\n", .{@max(@as(i32, -5), @as(i32, 3))});   // 3
    std.debug.print("max(1.5, 2.7) = {d}\n", .{@max(@as(f64, 1.5), @as(f64, 2.7))}); // 2.7
}

Exemplo 2: Garantir valor mínimo

const std = @import("std");

const Config = struct {
    threads: u32,
    buffer_size: usize,
};

fn normalizar(config: Config) Config {
    return .{
        // Pelo menos 1 thread
        .threads = @max(config.threads, 1),
        // Buffer de pelo menos 4096 bytes
        .buffer_size = @max(config.buffer_size, 4096),
    };
}

pub fn main() void {
    const config = normalizar(.{ .threads = 0, .buffer_size = 100 });
    std.debug.print("Threads: {}, Buffer: {}\n", .{ config.threads, config.buffer_size });
    // Threads: 1, Buffer: 4096
}

Exemplo 3: Encontrar máximo em slice

const std = @import("std");

fn maximo(valores: []const i32) ?i32 {
    if (valores.len == 0) return null;

    var max_val = valores[0];
    for (valores[1..]) |v| {
        max_val = @max(max_val, v);
    }
    return max_val;
}

pub fn main() void {
    const dados = [_]i32{ 5, -3, 12, 8, -1, 20, 7 };
    if (maximo(&dados)) |m| {
        std.debug.print("Máximo: {}\n", .{m}); // 20
    }
}

Casos de uso comuns

  1. Valor mínimo garantido: Garantir que configurações não fiquem abaixo de um limite.
  2. Clamping: Combinar com @min para restringir valores a um intervalo.
  3. Redução: Encontrar o maior valor em uma coleção.
  4. Layout: Calcular tamanho máximo necessário para buffers ou contêineres.

Comportamento com floats e NaN

Com valores de ponto flutuante, @max segue a semântica do IEEE 754: se um dos operandos for NaN, o resultado é o outro operando (não NaN). Isso difere de algumas implementações de max em outras linguagens que propagam NaN:

const nan = std.math.nan(f64);
const resultado = @max(nan, 5.0); // resultado == 5.0 (NaN não se propaga)
const resultado2 = @max(5.0, nan); // resultado2 == 5.0

Esse comportamento pode ser surpreendente, mas é consistente com a especificação IEEE 754-2008 para maxNum.

Comparação com C equivalente

Em C, não existe uma função max na biblioteca padrão para todos os tipos. Desenvolvedores usam macros, funções inline ou a expressão ternária:

// C — macro com problema de avaliação dupla
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int x = MAX(a++, b++); // BUG: a ou b pode ser incrementado duas vezes!

// C — função inline segura, mas verbosa e específica para cada tipo
static inline int max_int(int a, int b) { return a > b ? a : b; }

Em Zig, @max é um builtin genérico que funciona com qualquer tipo numérico, sem macros e sem avaliação dupla:

// Zig — simples, seguro, genérico
const maior = @max(a, b); // funciona para qualquer tipo numérico

Usando @max para clamping

A combinação de @max e @min é o idioma padrão para restringir valores a um intervalo (clamping):

fn clamp(comptime T: type, valor: T, minimo: T, maximo: T) T {
    return @max(minimo, @min(valor, maximo));
}

// Exemplos de uso:
const volume = clamp(f32, entrada_usuario, 0.0, 1.0);
const indice = clamp(usize, indice_calculado, 0, array.len - 1);
const temperatura = clamp(i16, leitura_sensor, -40, 85);

A biblioteca padrão do Zig também oferece std.math.clamp que faz exatamente isso.

Perguntas Frequentes

P: @max funciona com vetores SIMD (@Vector)?

Sim. @max é compatível com tipos @Vector, e o compilador pode gerar instruções SIMD específicas (como vmaxps em AVX) para comparar múltiplos valores simultaneamente. Isso é mais eficiente do que comparar elementos individualmente.

P: O que acontece se os dois valores forem iguais?

Se a == b, @max retorna um dos dois valores (ambos são equivalentes). O compilador pode retornar qualquer um dos operandos; não há garantia de qual será retornado quando iguais.

P: Posso usar @max com tipos inteiros com e sem sinal?

Os dois operandos devem ter o mesmo tipo. Se você tiver um u32 e um i32, é necessário converter um deles primeiro com @as ou @intCast. O compilador gera um erro claro se os tipos forem incompatíveis.

Builtins relacionados

  • @min — Retorna o menor de dois valores
  • @addWithOverflow — Aritmética com detecção de overflow
  • @as — Conversão de tipo

Tutoriais relacionados

Continue aprendendo Zig

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