@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(¬as);
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
- Cálculos matemáticos: Converter contadores, índices ou medições inteiras para float antes de operações de divisão, raiz quadrada, etc.
- Processamento de áudio: Converter amostras PCM inteiras para valores normalizados de ponto flutuante.
- Gráficos e renderização: Converter dimensões de pixel (inteiros) para coordenadas de mundo (float).
- Estatística: Calcular médias, desvios padrão e outras medidas a partir de dados inteiros.
- 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