Técnicas e precauções de precisão numérica em contratos inteligentes Rust

Diário de Desenvolvimento de Contratos Inteligentes Rust (7): Cálculo de Valores

Este artigo irá explorar os problemas de cálculo numérico na programação de contratos inteligentes em Rust, incluindo principalmente a questão da precisão das operações de ponto flutuante, a questão da precisão dos cálculos inteiros, e como escrever contratos inteligentes Rust para cálculos numéricos.

1. Problemas de precisão em operações com números 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 são inevitáveis. Ao escrever contratos inteligentes, não é recomendado usar operações com números de ponto flutuante, especialmente ao lidar com razões ou taxas de 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 para a sua representação. No entanto, alguns números decimais (, como 0.7), não podem ser representados com precisão usando um número finito de bits, resultando em fenômenos de "arredondamento".

Usando como exemplo a distribuição de 0,7 tokens NEAR para dez usuários na blockchain NEAR, os resultados dos cálculos podem apresentar imprecisões.

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

O resultado da execução mostra que o valor de amount não é exatamente 0.7, mas sim um valor extremamente próximo de 0.69999999999999995559. O resultado de uma operação de divisão posterior também se torna impreciso, resultando em 0.06999999999999999, em vez do esperado 0.07.

Para resolver este problema, pode-se considerar o uso de números fixos. No NEAR Protocol, normalmente utiliza-se 10^24 como denominador, ou seja, 10^24 yoctoNEAR equivalem a 1 token NEAR.

O código de teste modificado é o seguinte:

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, ""); }

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

2. Problema de precisão no cálculo de inteiros em Rust

Embora a utilização de operações inteiras possa resolver o problema da perda de precisão de números de ponto flutuante em certos cenários, os resultados dos cálculos inteiros também não são completamente precisos e confiáveis. Algumas das razões que afetam a precisão dos cálculos inteiros incluem:

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 #[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,"");

}

Os resultados dos testes mostram que os resultados de cálculo de result_0 e result_1 são diferentes. Isso ocorre porque, para a divisão inteira, a precisão abaixo do divisor será descartada. Ao calcular result_1, (a / b) perderá primeiro a precisão do cálculo, tornando-se 0; enquanto ao calcular result_0, calcular a * c primeiro pode evitar a perda de precisão.

2.2 quantidade muito pequena

Quando se trata de cálculos em ordens de grandeza menores, também podem surgir problemas 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 dos testes mostram que os resultados das operações result_0 e result_1 são diferentes, e que result_1 = 13 está mais próximo do valor de cálculo esperado de 13.3333...

3. Como escrever contratos inteligentes Rust para avaliação numérica

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

3.1 Ajustar a ordem das operações

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

3.2 aumentar a ordem de grandeza dos inteiros

Usar uma maior magnitude para criar moléculas maiores. Por exemplo, 5.123 NEAR pode ser representado como 5.123 * 10^10 = 51_230_000_000.

3.3 Perda de precisão de cálculo acumulada

Para problemas de precisão de cálculo de inteiros que não podem ser evitados, pode-se considerar a registar a perda acumulada de precisão nas operações. 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 e redistribuir os tokens não distribuídos devido à perda de precisão.

( 3.4 Usando a biblioteca Rust Crate rust-decimal

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

) 3.5 Considere o mecanismo de arredondamento

Ao projetar contratos inteligentes, o problema de arredondamento geralmente segue o princípio "quero lucrar, os outros não devem tirar vantagem de mim". De acordo com esse princípio, se o arredondamento para baixo for favorável ao contrato, então é para baixo; se o arredondamento para cima for favorável ao contrato, então é para cima; o arredondamento convencional é raramente adotado, pois não se pode determinar a quem é favorável.

Ao adotar esses métodos, é possível implementar cálculos numéricos mais precisos em contratos inteligentes Rust, aumentando a fiabilidade e a equidade do contrato.

![]###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
ZeroRushCaptainvip
· 07-17 09:17
Ah, já estou a sofrer com isto outra vez. Da última vez, foi um problema de precisão que resultou em Posição de bloqueio e perdi tudo.
Ver originalResponder0
BlockchainFriesvip
· 07-16 10:52
Este negócio de ponto flutuante é muito problemático. Quem usa, se dá mal.
Ver originalResponder0
MoonBoi42vip
· 07-15 08:48
A precisão dos números de ponto flutuante não é tão agradável quanto a dos números fixos~
Ver originalResponder0
OnchainDetectivevip
· 07-14 09:54
É evidente que o erro de precisão é a brecha que alguns pontes de cadeia cruzada foram exploradas por hackers, é de arrepiar.
Ver originalResponder0
HashRateHermitvip
· 07-14 09:54
O problema de precisão realmente me matou.
Ver originalResponder0
GateUser-e87b21eevip
· 07-14 09:47
Outra vez com a questão dos pontos flutuantes, estou confuso.
Ver originalResponder0
BearMarketSurvivorvip
· 07-14 09:45
A precisão é uma armadilha da qual ninguém pode escapar, esta pessoa entende.
Ver originalResponder0
AirdropLickervip
· 07-14 09:35
O arredondamento de números de ponto flutuante é muito complicado. Já caí nessa armadilha antes.
Ver originalResponder0
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)