comptime_float em Zig — O que é e Como Usar
Definição
comptime_float é o tipo de ponto flutuante de alta precisão que existe apenas em tempo de compilação. Quando você escreve um literal como 3.14 ou 2.71828 em Zig, ele é inicialmente do tipo comptime_float. Internamente, o compilador usa representação de 128 bits (conforme IEEE 754), permitindo cálculos em comptime com muito mais precisão do que f32 ou f64.
Assim como comptime_int, o comptime_float não existe em runtime. Antes da execução, ele deve ser convertido para um tipo concreto como f32 ou f64.
Por que comptime_float Importa
- Precisão máxima em comptime: Constantes matemáticas e cálculos intermediários mantêm alta precisão até serem convertidos.
- Literais flexíveis: O valor
3.14pode ser atribuído tanto af32quanto af64sem cast explícito. - Tabelas pré-calculadas: Permite gerar lookup tables com cálculos precisos durante a compilação.
- Sem erro de arredondamento prematuro: O arredondamento só ocorre quando o valor é convertido para o tipo concreto.
Exemplo Prático
Literais Float e Coerção Automática
const std = @import("std");
pub fn main() void {
// 3.14 é comptime_float, convertido para cada tipo
const a: f32 = 3.14;
const b: f64 = 3.14;
std.debug.print("f32: {d:.10}\n", .{a}); // menos precisão
std.debug.print("f64: {d:.10}\n", .{b}); // mais precisão
}
Constantes Matemáticas em Comptime
const std = @import("std");
const PI = 3.14159265358979323846;
const E = 2.71828182845904523536;
// Cálculos em comptime com alta precisão
const TAU = 2.0 * PI;
const GRAUS_PARA_RAD = PI / 180.0;
pub fn main() void {
const angulo_graus: f64 = 45.0;
const angulo_rad: f64 = angulo_graus * GRAUS_PARA_RAD;
std.debug.print("45° = {d:.6} rad\n", .{angulo_rad});
std.debug.print("TAU = {d:.10}\n", .{@as(f64, TAU)});
}
Tabela de Senos em Comptime
const std = @import("std");
const TAMANHO_TABELA = 8;
const tabela_seno = blk: {
var tabela: [TAMANHO_TABELA]f64 = undefined;
for (0..TAMANHO_TABELA) |i| {
const angulo = @as(f64, @floatFromInt(i)) * (2.0 * std.math.pi / @as(f64, TAMANHO_TABELA));
tabela[i] = @sin(angulo);
}
break :blk tabela;
};
pub fn main() void {
for (tabela_seno, 0..) |valor, i| {
std.debug.print("sen[{}] = {d:.4}\n", .{ i, valor });
}
}
comptime_float vs f32/f64
| Característica | comptime_float | f32 | f64 |
|---|---|---|---|
| Precisão | ~128 bits | 32 bits | 64 bits |
| Existe em runtime | Nunca | Sim | Sim |
| Erro de arredondamento | Mínimo | Significativo | Moderado |
| Operações de I/O | Não | Sim | Sim |
Verificando o Tipo em Comptime
Em contextos comptime, você pode verificar se um valor é comptime_float e tomar decisões baseadas no tipo:
const std = @import("std");
fn descricaoTipo(comptime T: type) []const u8 {
return switch (T) {
comptime_float => "literal float de alta precisão",
f16 => "float 16 bits",
f32 => "float 32 bits",
f64 => "float 64 bits",
f128 => "float 128 bits",
else => "outro tipo",
};
}
pub fn main() void {
std.debug.print("{s}\n", .{descricaoTipo(comptime_float)}); // literal float...
std.debug.print("{s}\n", .{descricaoTipo(f64)}); // float 64 bits
}
Precisão em Constantes Físicas e Matemáticas
Uma aplicação prática do comptime_float é definir constantes científicas com a máxima precisão disponível, convertendo para o tipo adequado apenas no ponto de uso:
// Constantes em comptime_float — precisão máxima (~128 bits internamente)
const VELOCIDADE_LUZ = 299_792_458.0; // m/s (exato)
const CONSTANTE_PLANCK = 6.62607015e-34; // J·s
const NUMERO_AVOGADRO = 6.02214076e23; // mol⁻¹
const CONSTANTE_GRAVITACIONAL = 6.67430e-11; // m³ kg⁻¹ s⁻²
// Uso: a conversão de precisão ocorre aqui, não na definição
const velocidade_f64: f64 = VELOCIDADE_LUZ;
const velocidade_f32: f32 = VELOCIDADE_LUZ;
// A mesma constante é usada em ambos sem cast manual
Coerção Automática e Inferência de Tipo
O comptime_float coage automaticamente para qualquer tipo float concreto, e o compilador infere o tipo correto pelo contexto:
fn escalar(comptime fator: comptime_float, dados: []f64) void {
for (dados) |*d| {
d.* *= fator; // fator é convertido para f64 automaticamente
}
}
fn escalarF32(comptime fator: comptime_float, dados: []f32) void {
for (dados) |*d| {
d.* *= fator; // fator é convertido para f32 automaticamente
}
}
// Mesma constante, conversão automática para o tipo correto
const DOBRO = 2.0;
// escalar(DOBRO, ...) usa f64; escalarF32(DOBRO, ...) usa f32
Armadilhas Comuns
- Não pode existir em runtime: Variáveis
comptime_floatem funções normais causam erro. Devem ser convertidas paraf32ouf64. - Perda de precisão: Ao converter de
comptime_floatparaf32, há perda de precisão. Usef64quando precisar de mais dígitos significativos. - Comparação com zero: Cálculos em comptime podem dar resultado exato onde runtime teria erro de arredondamento. Não assuma que o comportamento em comptime será igual ao de runtime.
- Não confunda com
f128:comptime_floatef128são tipos distintos.f128existe em runtime e pode ser usado em variáveis e estruturas;comptime_floatexiste apenas em comptime e não ocupa espaço em memória.
Termos Relacionados
- comptime_int — Tipo inteiro de precisão arbitrária em comptime
- Comptime — Execução em tempo de compilação
- Type Coercion — Regras de conversão automática de tipos
- anytype — Parâmetro de tipo inferido