Introdução
Tipos numéricos comuns como u64 e i128 têm limites de tamanho. Quando você precisa trabalhar com números que excedem esses limites – como em criptografia, fatoriais ou matemática combinatória – a biblioteca padrão do Zig oferece std.math.big.int.Managed para inteiros de precisão arbitrária.
Nesta receita, você aprenderá a criar e manipular big integers em Zig.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja o guia de instalação
- Conhecimento básico de Zig. Consulte a introdução ao Zig
- Familiaridade com alocadores
Operações Básicas com Big Integer
const std = @import("std");
const Managed = std.math.big.int.Managed;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Criar big integers
var a = try Managed.initSet(allocator, 1000000000);
defer a.deinit();
var b = try Managed.initSet(allocator, 999999999);
defer b.deinit();
// Adição
var soma = try Managed.init(allocator);
defer soma.deinit();
try soma.add(&a, &b);
// Multiplicação
var produto = try Managed.init(allocator);
defer produto.deinit();
try produto.mul(&a, &b);
// Converter para string para exibir
const soma_str = try soma.toString(allocator, 10, .lower);
defer allocator.free(soma_str);
const produto_str = try produto.toString(allocator, 10, .lower);
defer allocator.free(produto_str);
std.debug.print("a = 1000000000\n", .{});
std.debug.print("b = 999999999\n", .{});
std.debug.print("a + b = {s}\n", .{soma_str});
std.debug.print("a * b = {s}\n", .{produto_str});
}
Calcular Fatorial
Fatoriais crescem muito rápido e rapidamente excedem limites de tipos fixos:
const std = @import("std");
const Managed = std.math.big.int.Managed;
fn fatorial(allocator: std.mem.Allocator, n: u32) !Managed {
var resultado = try Managed.initSet(allocator, 1);
errdefer resultado.deinit();
var i: u32 = 2;
while (i <= n) : (i += 1) {
var temp = try Managed.initSet(allocator, i);
defer temp.deinit();
var novo = try Managed.init(allocator);
try novo.mul(&resultado, &temp);
resultado.deinit();
resultado = novo;
}
return resultado;
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const valores = [_]u32{ 10, 20, 50, 100 };
for (&valores) |n| {
var fat = try fatorial(allocator, n);
defer fat.deinit();
const str = try fat.toString(allocator, 10, .lower);
defer allocator.free(str);
std.debug.print("{d}! = {s}\n", .{ n, str });
std.debug.print(" ({d} dígitos)\n\n", .{str.len});
}
}
Potência de Big Integer
const std = @import("std");
const Managed = std.math.big.int.Managed;
fn potencia(allocator: std.mem.Allocator, base_val: u64, exp: u32) !Managed {
var resultado = try Managed.initSet(allocator, 1);
errdefer resultado.deinit();
var base = try Managed.initSet(allocator, base_val);
defer base.deinit();
var i: u32 = 0;
while (i < exp) : (i += 1) {
var novo = try Managed.init(allocator);
try novo.mul(&resultado, &base);
resultado.deinit();
resultado = novo;
}
return resultado;
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// 2^64 (excede u64)
var p1 = try potencia(allocator, 2, 64);
defer p1.deinit();
const s1 = try p1.toString(allocator, 10, .lower);
defer allocator.free(s1);
std.debug.print("2^64 = {s}\n", .{s1});
// 2^128
var p2 = try potencia(allocator, 2, 128);
defer p2.deinit();
const s2 = try p2.toString(allocator, 10, .lower);
defer allocator.free(s2);
std.debug.print("2^128 = {s}\n", .{s2});
// 10^30
var p3 = try potencia(allocator, 10, 30);
defer p3.deinit();
const s3 = try p3.toString(allocator, 10, .lower);
defer allocator.free(s3);
std.debug.print("10^30 = {s}\n", .{s3});
}
Criar Big Integer a partir de String
const std = @import("std");
const Managed = std.math.big.int.Managed;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Criar a partir de string grande
var num = try Managed.init(allocator);
defer num.deinit();
try num.setString(10, "123456789012345678901234567890");
const str = try num.toString(allocator, 10, .lower);
defer allocator.free(str);
std.debug.print("Número: {s}\n", .{str});
// Hexadecimal
var hex_num = try Managed.init(allocator);
defer hex_num.deinit();
try hex_num.setString(16, "FFFFFFFFFFFFFFFFFFFFFFFF");
const hex_dec = try hex_num.toString(allocator, 10, .lower);
defer allocator.free(hex_dec);
const hex_str = try hex_num.toString(allocator, 16, .upper);
defer allocator.free(hex_str);
std.debug.print("Hex: {s}\n", .{hex_str});
std.debug.print("Dec: {s}\n", .{hex_dec});
}
Comparação de Big Integers
const std = @import("std");
const Managed = std.math.big.int.Managed;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var a = try Managed.init(allocator);
defer a.deinit();
try a.setString(10, "99999999999999999999");
var b = try Managed.init(allocator);
defer b.deinit();
try b.setString(10, "100000000000000000000");
const ordem = a.order(b);
switch (ordem) {
.lt => std.debug.print("a < b\n", .{}),
.eq => std.debug.print("a == b\n", .{}),
.gt => std.debug.print("a > b\n", .{}),
}
// Verificar igualdade
var c = try Managed.initSet(allocator, 42);
defer c.deinit();
var d = try Managed.initSet(allocator, 42);
defer d.deinit();
std.debug.print("42 == 42? {}\n", .{c.order(d) == .eq});
}
Dicas e Boas Práticas
Gerencie a memória: Cada
Managedaloca memória. Usedefer deinit()sempre.Operações criam novos valores: Use variáveis temporárias e libere-as adequadamente.
ArenaAllocator facilita: Para cálculos complexos com muitos intermediários, use ArenaAllocator.
toString aloca memória: O resultado precisa ser liberado com
allocator.free().Performance: Big integers são mais lentos que tipos nativos. Use
u128oui128se couber.
Receitas Relacionadas
- Operações Matemáticas - Funções matemáticas
- Aritmética Segura (Overflow) - Overflow seguro
- Usando ArenaAllocator - Simplificar alocação
- Converter entre Bases Numéricas - Bases numéricas