@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
- Valor mínimo garantido: Garantir que configurações não fiquem abaixo de um limite.
- Clamping: Combinar com
@minpara restringir valores a um intervalo. - Redução: Encontrar o maior valor em uma coleção.
- 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