Desenvolvimento de contratos inteligentes em Rust: técnicas de cálculo numérico e controle de precisão

Diário de Desenvolvimento de Contratos Inteligentes Rust (7): Cálculo Numérico

1. Problemas de precisão em operações com números de ponto flutuante

A linguagem Rust suporta operações com números de ponto flutuante de forma nativa, mas essas operações apresentam problemas de precisão de cálculo que não podem ser evitados. Ao escrever contratos inteligentes, não é recomendado usar operações com números de ponto flutuante, especialmente ao lidar com taxas ou juros que envolvam decisões econômicas/financeiras importantes.

O tipo de ponto flutuante de dupla precisão f64 na linguagem Rust segue o padrão IEEE 754, utilizando a notação científica com base 2. Certos números decimais, como 0.7, não podem ser representados com precisão por números de ponto flutuante de comprimento finito, existindo o fenômeno de "arredondamento".

No teste de distribuição de 0,7 tokens NEAR para dez usuários na blockchain NEAR, o resultado da operação de ponto flutuante não é preciso:

ferrugem let amount: f64 = 0.7;
let divisor: f64 = 10.0; let result_0 = amount / divisor;

o valor de amount é 0.69999999999999995559, o valor de result_0 é 0.06999999999999999, em vez do esperado 0.07.

Para resolver este problema, pode-se usar números de ponto fixo. No NEAR Protocol, normalmente utiliza-se a representação 1 NEAR = 10^24 yoctoNEAR:

ferrugem deixe N: u128 = 1_000_000_000_000_000_000_000_000; let amount: u128 = 700_000_000_000_000_000_000_000; let divisor: u128 = 10; let result_0 = amount / divisor;

Desta forma, pode-se obter resultados de cálculo precisos: 0,7 NEAR / 10 = 0,07 NEAR.

2. Problema de precisão na computação de inteiros em Rust

2.1 Ordem das operações

A mudança na ordem de multiplicação e divisão com a mesma prioridade aritmética pode afetar diretamente o resultado do cálculo. Por exemplo:

ferrugem let a: u128 = 1_0000; let b: u128 = 10_0000; let c: u128 = 20;

// result_0 = a * c / b let result_0 = a.checked_mul(c).expect("ERR_MUL").checked_div(b).expect("ERR_DIV");

// result_1 = a / b * c
let result_1 = a.checked_div(b).expect("ERR_DIV").checked_mul(c).expect("ERR_MUL");

o resultado de result_0 e result_1 é diferente, porque para a divisão inteira, a precisão abaixo do divisor é descartada.

2.2 quantidade muito pequena

Quando se trata de valores decimais, a operação inteira pode resultar em perda de precisão:

ferrugem let a: u128 = 10; let b: u128 = 3; let c: u128 = 4; let decimal: u128 = 100_0000;

// result_0 = (a / b) * c let result_0 = a.checked_div(b).expect("ERR_DIV").checked_mul(c).expect("ERR_MUL");

// result_1 = (a * decimal / b) * c / decimal;
let result_1 = a.checked_mul(decimal).expect("ERR_MUL") .checked_div(b).expect("ERR_DIV") .checked_mul(c).expect("ERR_MUL") .checked_div(decimal).expect("ERR_DIV");

o resultado da operação entre result_0 e result_1 é diferente, result_1 está mais próximo do valor esperado real.

3. Como escrever contratos inteligentes Rust para cálculos numéricos

3.1 Ajustar a ordem das operações

Fazer com que a multiplicação de inteiros tenha prioridade sobre a divisão de inteiros.

3.2 aumentar a ordem de grandeza dos inteiros

Utilize uma escala maior para criar moléculas maiores. Por exemplo, expresse 5.123 NEAR como 51_230_000_000 yoctoNEAR.

3.3 Perda de precisão dos cálculos acumulados

Registar a perda de precisão acumulada nos cálculos e compensá-la em cálculos subsequentes. Por exemplo:

ferrugem u128 { let token_to_distribute = offset + amount; let per_user_share = token_to_distribute / USER_NUM; let recorded_offset = token_to_distribute - per_user_share * USER_NUM; recorded_offset }

( 3.4 Usando a biblioteca Rust Crate rust-decimal

Esta biblioteca é adequada para cálculos financeiros em decimais que requerem precisão eficaz e não têm erro de arredondamento.

) 3.5 Considerar o mecanismo de arredondamento

No design de contratos inteligentes, o problema de arredondamento geralmente adota o princípio "quero lucrar, os outros não devem tirar vantagem de mim". Escolha arredondar para baixo, arredondar para cima ou arredondar para o mais próximo, conforme a situação.

![]###https://img-cdn.gateio.im/webp-social/moments-6e8b4081214a69423fc7ae022d05c728.webp###

TOKEN4.89%
NUM0.97%
Ver original
Esta página pode conter conteúdo de terceiros, que é fornecido apenas para fins informativos (não para representações/garantias) e não deve ser considerada como um endosso de suas opiniões pela Gate nem como aconselhamento financeiro ou profissional. Consulte a Isenção de responsabilidade para obter detalhes.
  • Recompensa
  • 3
  • Compartilhar
Comentário
0/400
BearMarketBuildervip
· 08-05 07:58
Os números de ponto flutuante são um buraco tão profundo como o mar.
Ver originalResponder0
SelfSovereignStevevip
· 08-05 07:53
Os números de ponto flutuante são muito problemáticos. Os contratos inteligentes vão falhar. Não me atrevo a usá-los.
Ver originalResponder0
DegenGamblervip
· 08-05 07:49
Atenção irmãos que jogam Rust, a precisão é uma armadilha considerável.
Ver originalResponder0
  • Marcar
Faça trade de criptomoedas em qualquer lugar e a qualquer hora
qrCode
Escaneie o código para baixar o app da Gate
Comunidade
Português (Brasil)
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)