Continuando a nossa saga de entender como são realizadas as transações financeiras, hoje vamos explorar um aspecto crítico no desenvolvimento de sistemas financeiros: como garantir a precisão nos cálculos de valores monetários.
Entendendo o que é precisão arbitrária
Um dos conceitos importantes quando falamos de transações financeiras é a precisão arbitrária, que permite que os cálculos matemáticos sejam realizados com exatidão absoluta, sem limitações de tamanho ou escala. Isso é especialmente crucial em sistemas financeiros, onde até mesmo pequenos erros de arredondamento podem resultar em discrepâncias significativas ao longo do tempo.
O problema da precisão
Em Go, se usarmos float64
para somar valores monetários, podemos obter resultados imprecisos devido à forma como números de ponto flutuante são representados na memória.
package main
import (
"fmt"
)
func main() {
a := 0.1
b := 0.2
fmt.Println(a + b) // 0.30000000000000004 ❌ (Erro de precisão)
}
Por que esse erro acontece?
Números de ponto flutuante (float64
) são representados internamente usando o padrão IEEE 754, que usa uma aproximação binária para armazenar valores decimais. No entanto, números como 0.1
e 0.2
não têm uma representação exata em binário, resultando em pequenas imprecisões ao somar.
Isso pode ser um grande problema em aplicações financeiras, pois pequenos erros podem se acumular ao longo de muitas transações.
Solução: Usando math/big
para Precisão Arbitrária
Em Go, a biblioteca math/big
oferece o tipo big.Float
, que permite cálculos com precisão arbitrária:
package main
import (
"fmt"
"math/big"
)
func main() {
a := big.NewFloat(0.1)
b := big.NewFloat(0.2)
result := new(big.Float).Add(a, b)
fmt.Println(result.Text('f', 10)) // "0.3" ✅ (Precisão exata)
}
Aqui, big.NewFloat(0.1)
armazena o número com precisão arbitrária, evitando erros de arredondamento.
Armazenando centavos como inteiros
No nosso caso, dentro do Midaz, armazenamos todos os valores monetários como uma estrutura que combina um valor inteiro (Amount) e uma escala (Scale).
type Amount struct {
Amount *int64 `json:"amount" example:"1500"`
Scale *int64 `json:"scale" example:"2"`
}
No código acima, o Amount representa o valor monetário multiplicado pela escala (10^n), e o Scale define o número de casas decimais. Por exemplo, para representar R$ 15,00, teríamos Amount = 1500
e Scale = 2
. Esta estrutura nos permite trabalhar com valores monetários de forma precisa e flexível, adaptando-se a diferentes moedas e requisitos de precisão.
Esta abordagem é particularmente interessante para valores cuja precisão varia, como é o caso do Bitcoin, em que é necessário trabalhar com até 8 casas decimais. Para entender mais sobre o Amount
e Scale
, recomendo a leitura da nossa documentação.
E você, já teve que lidar com cálculos monetários com esse nível de precisão? Como foi sua experiência? Compartilhe suas experiências nos comentários abaixo!
Top comments (0)