[Russ Cox] A revolução na conversão de ponto flutuante: mais fácil, mais rápida e mais simples
(research.swtch.com)Resumo:
- O novo algoritmo de conversão de ponto flutuante proposto por Russ Cox é mais simples e, ao mesmo tempo, oferece melhor desempenho do que algoritmos complexos existentes (Ryū, Dragonbox etc.).
- Por meio do primitivo central
Unrounded Scaling, ele acelera a conversão entre binário e decimal para um nível equivalente a uma única multiplicação de 64 bits. - A implementação deve ser incluída no Go 1.27 (previsto para agosto de 2026) e oferece suporte tanto a saída de precisão fixa quanto a parsing/saída de menor comprimento que preserva o valor original.
- Melhoria de desempenho em relação às implementações existentes
- Desempenho de saída (Printing): melhora de cerca de 10~30%
- Desempenho de parsing (Parsing): melhora de cerca de 5~15%
Resumo detalhado:
1. Contexto: a complexidade crônica da conversão de ponto flutuante
A tarefa de converter números de ponto flutuante binários do computador para decimais legíveis por humanos, ou fazer o caminho inverso, é uma área em que diversos algoritmos (Dragon4, Grisu3, Ryū, Dragonbox etc.) competem há décadas. No passado, Russ Cox disse que “converter é fácil, o problema é a velocidade”, mas neste post ele demonstra que “um algoritmo de conversão rápido também pode ser simples”.
2. Ideia central: Unrounded Numbers (⟨x⟩)
A base desse algoritmo é o tipo unrounded. Ele mantém não apenas a parte inteira do número, mas também 2 bits de informação adicional que incluem os dados necessários para arredondamento (o bit de ½ e o valor OR dos bits abaixo dele, chamado de 'sticky bit').
- Estrutura:
⟨x⟩ = ⌊4x⌋ | (4x ≠ ⌊4x⌋) - Vantagem: ao manter essa forma, é possível executar operações como floor, ceil ou a mais importante em ponto flutuante, 'Round-to-nearest, ties-to-even', com custo muito baixo.
3. Escalonamento não arredondado rápido (Fast Unrounded Scaling)
A função mais importante é uscale(x, e, p). Ela calcula o resultado unrounded.
Algoritmos anteriores exigiam operações com inteiros muito grandes, mas esta abordagem processa isso dentro de operações de 64 bits com base no seguinte princípio.
// Exemplo de implementação conceitual (a versão otimizada real é mais complexa)
func uscale(x uint64, e, p int) unrounded {
// Calcula aproximando 10^p como 2^k * fração exata
// Na maioria dos casos, reduz-se a uma única multiplicação de 64 bits (mul64) e operações de shift
}
4. Principais resultados e desempenho
- Simplicidade: o código de implementação é muito curto, o que facilita a manutenção e permite prova lógica.
- Desempenho: segundo os benchmarks, ele apresenta desempenho superior aos algoritmos considerados os mais rápidos atualmente,
Dragonbox(saída) eEisel-Lemire(parsing). - Versatilidade: aplica-se tanto à saída em ponto fixo (Fixed-width printing) quanto à saída de menor largura que recupera perfeitamente o valor original (Shortest-width printing).
5. O que isso significa para desenvolvedores
Esse algoritmo deve ser integrado à biblioteca padrão da linguagem Go. Quando a conversão decimal ocorrer internamente (por exemplo, fmt.Printf("%f", f) ou strconv.ParseFloat), desenvolvedores poderão obter resultados corretos usando menos recursos de CPU. Além disso, o conceito de unrounded oferece inspiração para um controle numérico preciso mesmo sem bibliotecas complexas de análise numérica.
Ainda não há comentários.