Desenvolvimento de contratos inteligentes em Rust: Superando o desafio da precisão dos cálculos numéricos

Problemas de precisão nos cálculos numéricos no desenvolvimento de contratos inteligentes em Rust

1. Problemas de precisão em operações de ponto flutuante

A linguagem Rust suporta nativamente operações com números de ponto flutuante, mas essas operações apresentam problemas de precisão de cálculo que não podem ser evitados. Ao escrever contratos inteligentes, não se recomenda o uso de operações com números de ponto flutuante, especialmente ao lidar com taxas ou juros que envolvem 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 não podem ser representados com precisão por números de ponto flutuante de comprimento finito, resultando em um fenômeno de "arredondamento".

Por exemplo, ao distribuir 0.7 tokens NEAR na blockchain NEAR para 10 usuários:

ferrugem #[test] fn precision_test_float() { let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
println!("O valor de montante: {:.20}", montante); assert_eq!(result_0, 0.07, ""); }

Os resultados mostram que o valor de amount é 0.69999999999999995559, não o exato 0.7. O resultado da operação de divisão também se tornou impreciso, sendo 0.06999999999999999, em vez do esperado 0.07.

Para resolver este problema, pode-se considerar o uso de números fixos. No NEAR Protocol, é comum usar 10^24 yoctoNEAR para representar 1 token NEAR. Código de teste modificado:

ferrugem #[test] fn precision_test_integer() { let 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; assert_eq!(result_0, 70_000_000_000_000_000_000_000, ""); }

Dessa forma, é possível obter um resultado de cálculo preciso: 0,7 NEAR / 10 = 0,07 NEAR.

2. Problema de precisão em cálculos inteiros com Rust

A utilização de operações inteiras pode resolver o problema de perda de precisão nas operações de ponto flutuante em certos cenários, mas os resultados dos cálculos inteiros também não são completamente precisos e fiáveis.

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, levando a problemas de precisão no cálculo de inteiros. Por exemplo:

ferrugem #[test] fn precision_test_div_before_mul() { let a: u128 = 1_0000; let b: u128 = 10_0000; let c: u128 = 20;

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

assert_eq!(result_0,result_1,"");

}

O resultado da execução mostra que result_0 e result_1 não são iguais. A razão é que a divisão inteira descarta a precisão abaixo do divisor. Ao calcular result_1, (a / b) perde primeiro a precisão e se torna 0; enquanto ao calcular result_0, primeiro se calcula a * c, evitando a perda de precisão.

2.2 quantidade muito pequena

Quando se trata de cálculos decimais, a operação com inteiros pode levar à perda de precisão:

ferrugem #[test] fn precision_test_decimals() { let a: u128 = 10; let b: u128 = 3; let c: u128 = 4; let decimal: u128 = 100_0000;

let result_0 = a.checked_div(b).expect("ERR_DIV").checked_mul(c).expect("ERR_MUL");
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");

println!("{}:{}", result_0, result_1);
assert_eq!(result_0, result_1, "");

}

Os resultados mostram result_0=12, result_1=13, enquanto o valor esperado real deve ser 13.3333....

3. Como escrever contratos inteligentes de cálculo numérico em Rust

Para aumentar a precisão, podem ser adotadas as seguintes medidas de proteção:

3.1 Ajustar a ordem das operações

Deixe a multiplicação de inteiros ter prioridade sobre a divisão de inteiros.

3.2 aumentar a ordem de grandeza dos inteiros

Use uma magnitude maior para criar moléculas maiores. Por exemplo, representar 5.123 NEAR pode ser feito como 5.123 * 10^10 = 51_230_000_000 para participar de cálculos subsequentes.

3.3 perda de precisão em operações acumuladas

Para problemas de precisão inevitáveis, pode-se registar a perda acumulada de precisão nos cálculos. Por exemplo:

ferrugem const USER_NUM: u128 = 3;

u128 { let token_to_distribute = offset + amount; let per_user_share = token_to_distribute / USER_NUM; println!("per_user_share {}", per_user_share); let recorded_offset = token_to_distribute - per_user_share * USER_NUM; recorded_offset }

#( fn record_offset_test)[test] { let mut offset: u128 = 0; para i em 1..7 { println!("Round {}", i); offset = distribute(to_yocto)"10"(, offset(; println!)"Offset {}\n", offset); } }

Este método pode acumular os tokens restantes de cada distribuição e distribuí-los todos de uma vez na próxima distribuição, alcançando assim o objetivo de uma distribuição completa.

( 3.4 Usando a biblioteca Rust Crate rust-decimal

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

) 3.5 Considerar o mecanismo de arredondamento

Na concepção de contratos inteligentes, o problema de arredondamento geralmente adota o princípio de "quero me beneficiar, os outros não devem me explorar". Escolhe-se arredondar para baixo ou para cima, raramente se utiliza o arredondamento convencional.

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

Ver original
Esta página pode conter conteúdos de terceiros, que são fornecidos apenas para fins informativos (sem representações/garantias) e não devem ser considerados como uma aprovação dos seus pontos de vista pela Gate, nem como aconselhamento financeiro ou profissional. Consulte a Declaração de exoneração de responsabilidade para obter mais informações.
  • Recompensa
  • 8
  • Partilhar
Comentar
0/400
rugpull_survivorvip
· 07-14 15:34
Muitas pessoas caíram na armadilha de arredondamento.
Ver originalResponder0
WhaleWatchervip
· 07-14 07:46
O controle de precisão é fundamental.
Ver originalResponder0
consensus_failurevip
· 07-13 12:52
Mais seguro com precisão fixa
Ver originalResponder0
Ser_Liquidatedvip
· 07-12 19:41
Se houver outro problema de precisão, serei liquidado.
Ver originalResponder0
DecentralizeMevip
· 07-11 16:13
Substitua por números de ponto fixo.
Ver originalResponder0
BoredWatchervip
· 07-11 15:58
O código é um pouco complicado.
Ver originalResponder0
BrokenDAOvip
· 07-11 15:53
Poder de computação total excedente será problemático
Ver originalResponder0
MEVSandwichMakervip
· 07-11 15:51
É uma velha armadilha, sugiro que a evitem.
Ver originalResponder0
  • Pino
Negocie cripto em qualquer lugar e a qualquer hora
qrCode
Digitalizar para transferir a aplicação Gate
Novidades
Português (Portugal)
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)