@floatFromInt em Zig — Referência e Exemplos

@floatFromInt em Zig

O @floatFromInt converte um valor inteiro para um tipo de ponto flutuante. É a operação inversa de @intFromFloat. Essa conversão é necessária em Zig porque a linguagem não realiza conversões numéricas implícitas, mantendo todo o código explícito e previsível.

Sintaxe

@floatFromInt(valor: anytype) FloatType

O tipo de retorno é inferido pelo contexto.

O que faz

O @floatFromInt converte um valor inteiro (com ou sem sinal, de qualquer tamanho) para um tipo de ponto flutuante. Se o valor inteiro não puder ser representado exatamente no tipo float de destino (por exemplo, um i64 muito grande convertido para f32), o resultado é arredondado para o valor representável mais próximo.

Parâmetros

  • valor (anytype): Um valor inteiro de qualquer tipo (u8, i32, u64, comptime_int, etc.) a ser convertido para ponto flutuante.

Valor de retorno

Retorna o valor convertido como o tipo de ponto flutuante de destino, inferido pelo contexto.

Exemplos práticos

Exemplo 1: Conversões básicas

const std = @import("std");

test "conversão inteiro para float" {
    const a: i32 = 42;
    const b: u8 = 255;

    const x: f64 = @floatFromInt(a);
    const y: f32 = @floatFromInt(b);

    try std.testing.expect(x == 42.0);
    try std.testing.expect(y == 255.0);

    // Também funciona com negativos
    const c: i16 = -100;
    const z: f64 = @floatFromInt(c);
    try std.testing.expect(z == -100.0);
}

Exemplo 2: Cálculo de média

const std = @import("std");

fn media(valores: []const i32) f64 {
    var soma: i64 = 0;
    for (valores) |v| {
        soma += v;
    }
    // Converter soma e contagem para float antes de dividir
    const soma_f: f64 = @floatFromInt(soma);
    const n: f64 = @floatFromInt(valores.len);
    return soma_f / n;
}

test "calcular média" {
    const notas = [_]i32{ 85, 92, 78, 96, 88 };
    const resultado = media(&notas);
    try std.testing.expectApproxEqAbs(87.8, resultado, 0.001);
}

Exemplo 3: Normalização de valores

const std = @import("std");

fn normalizar(valor: u16) f32 {
    // Converter valor de 0-65535 para 0.0-1.0
    const max: f32 = @floatFromInt(std.math.maxInt(u16));
    const v: f32 = @floatFromInt(valor);
    return v / max;
}

fn normalizarParaAudio(amostra: i16) f32 {
    // Converter amostra de áudio PCM (-32768 a 32767) para -1.0 a 1.0
    const max: f32 = @floatFromInt(std.math.maxInt(i16));
    const v: f32 = @floatFromInt(amostra);
    return v / max;
}

test "normalização" {
    try std.testing.expectApproxEqAbs(0.0, normalizar(0), 0.001);
    try std.testing.expectApproxEqAbs(1.0, normalizar(65535), 0.001);
    try std.testing.expectApproxEqAbs(0.5, normalizar(32767), 0.01);

    try std.testing.expectApproxEqAbs(0.0, normalizarParaAudio(0), 0.001);
    try std.testing.expectApproxEqAbs(1.0, normalizarParaAudio(32767), 0.001);
}

Casos de uso comuns

  1. Cálculos matemáticos: Converter contadores, índices ou medições inteiras para float antes de operações de divisão, raiz quadrada, etc.
  2. Processamento de áudio: Converter amostras PCM inteiras para valores normalizados de ponto flutuante.
  3. Gráficos e renderização: Converter dimensões de pixel (inteiros) para coordenadas de mundo (float).
  4. Estatística: Calcular médias, desvios padrão e outras medidas a partir de dados inteiros.
  5. Progresso e porcentagens: Calcular porcentagem de conclusão dividindo valores inteiros.
fn progresso(atual: usize, total: usize) f64 {
    const a: f64 = @floatFromInt(atual);
    const t: f64 = @floatFromInt(total);
    return (a / t) * 100.0;
}

Observações sobre precisão

Tipos de ponto flutuante têm precisão limitada. Um f32 tem apenas 23 bits de mantissa, então inteiros maiores que 2^24 podem perder precisão ao serem convertidos. Para máxima precisão, use f64 (52 bits de mantissa) ou f128 quando necessário.

Comparação com C equivalente

Em C, a conversão de inteiro para float é implícita, o que pode mascarar erros:

int n = 1000000;
float resultado = n / 3; // Divisão inteira! resultado = 333333.0
float correto = (float)n / 3.0f; // OK — conversão explícita

Em Zig, não há conversão implícita, então o erro fica evidente em tempo de compilação:

const n: i32 = 1000000;
// const resultado = n / 3.0; // Erro de compilação — tipos incompatíveis
const correto: f32 = @floatFromInt(n) / 3.0; // OK — explícito e correto

A ausência de coerção implícita em Zig elimina bugs sutis de divisão inteira acidental.

Considerações de desempenho

A instrução @floatFromInt compila para uma única instrução de conversão de inteiro para float na maioria das arquiteturas (como cvtsi2ss em x86 para f32 ou cvtsi2sd para f64). O custo é mínimo — geralmente 1 ciclo de clock em CPUs modernas.

Para loops de alta performance que convertem muitos valores, considere usar SIMD via @Vector para converter múltiplos inteiros simultaneamente.

Erros comuns

Erro 1: Assumir que a conversão é sempre exata.

const grande: i64 = 123_456_789_012;
const f: f32 = @floatFromInt(grande); // IMPRECISO!
// f32 só tem 23 bits de mantissa — perda de precisão inevitável
const fd: f64 = @floatFromInt(grande); // Melhor, mas ainda pode perder precisão para i64 muito grandes

Erro 2: Confundir com @as para conversão entre tipos numéricos.

@as converte entre tipos sem mudar a representação semântica do valor quando possível. @floatFromInt é específico para a conversão de inteiro para ponto flutuante e deixa a intenção clara no código.

Erro 3: Esquecer a conversão em divisões mistas.

const total: u32 = 10;
const parte: u32 = 3;

// Sem conversão — divisão inteira!
const razao_errada = parte / total; // 0

// Com conversão — divisão de ponto flutuante
const razao: f64 = @as(f64, @floatFromInt(parte)) / @floatFromInt(total); // 0.3

Perguntas Frequentes

P: O que acontece se o inteiro for negativo e eu converter para float sem sinal?

Não existe float “sem sinal” em Zig (nem em IEEE 754). Todos os tipos float suportam valores negativos via bit de sinal. A conversão de um i32 negativo para f64 resultará no valor negativo correspondente como float.

P: @floatFromInt funciona com comptime_int?

Sim. Literais inteiros em Zig são comptime_int por padrão, e @floatFromInt aceita qualquer tipo inteiro, incluindo comptime. O resultado também será comptime se o input for comptime.

P: Qual a diferença entre @floatFromInt e @as(f64, valor_int)?

@as só funciona quando há uma coerção válida definida entre os tipos. Entre inteiros e floats, @as não funciona — é preciso usar @floatFromInt explicitamente. O uso de @floatFromInt também comunica melhor a intenção ao leitor do código.

Builtins relacionados

  • @intFromFloat — Operação inversa: converte float para inteiro
  • @as — Conversão de tipo genérica
  • @floatCast — Conversão entre tipos de float
  • @intFromEnum — Converte enum para inteiro

Tutoriais relacionados

Continue aprendendo Zig

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