Problemas de precisão em cálculos numéricos em contratos inteligentes Rust e soluções de otimização

robot
Geração do resumo em andamento

Diário de Desenvolvimento de Contratos Inteligentes em Rust (7) Problemas de Precisão em Operações com Números de Ponto Flutuante e Inteiros

Este artigo discutirá os problemas de precisão nas operações de ponto flutuante e inteiras em contratos inteligentes Rust, bem como como escrever contratos inteligentes de cálculo numérico.

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

A linguagem Rust suporta nativamente operações 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 é recomendado usar operações de ponto flutuante, especialmente ao lidar com taxas ou juros que envolvem decisões econômicas/financeiras importantes.

Na linguagem Rust, os números de ponto flutuante seguem o padrão IEEE 754, utilizando notação científica com base 2. Certas frações, como 0.7(, não podem ser representadas com precisão por números de ponto flutuante de comprimento finito, resultando em fenômenos de "arredondamento".

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

ferrugem #) fn precision_test_float[test]( { let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
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 aproximado de 0.69999999999999995559. O resultado da operação de divisão também não é preciso, sendo 0.06999999999999999 em vez do esperado 0.07.

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

ferrugem #) fn precision_test_integer[test]( { 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, ""(; }

Assim, o resultado do cálculo atuarial é: 0,7 NEAR / 10 = 0,07 NEAR.

![])https://img-cdn.gateio.im/webp-social/moments-7bdd27c1211e1cc345bf262666a993da.webp(

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

) 2.1 Ordem das operações

A ordem em que a multiplicação e a divisão, que têm a mesma prioridade aritmética, são realizadas pode afetar diretamente o resultado do cálculo:

ferrugem

fn precision_test_div_before_mul[test]( { 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 menor que o divisor. Ao calcular result_1, )a / b( perderá primeiro a precisão e se tornará 0; enquanto ao calcular result_0, calcular a * c primeiro pode evitar a perda de precisão.

) 2.2 quantidade muito pequena

Um tamanho de ordem muito pequeno também pode levar a problemas de precisão:

ferrugem

fn precision_test_decimals[test]( { 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"(;

assert_eq!)result_0, result_1, ""(;

}

Os resultados mostram result_0=12, result_1=13, sendo este último mais próximo do valor esperado 13.3333.

![])https://img-cdn.gateio.im/webp-social/moments-1933a4a2dd723a847f0059d31d1780d1.webp(

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

Para aumentar a precisão, podem ser tomadas 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 magnitude maior para criar moléculas maiores. Por exemplo, representar 5.123 NEAR como 5.123 * 10^10 = 51_230_000_000.

3.3 perda de precisão na acumulação de cálculos

Registar a perda de precisão acumulada dos cálculos:

ferrugem const USER_NUM: u128 = 3;

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 }

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

Dessa forma, os tokens não distribuídos podem ser armazenados temporariamente e distribuídos juntamente na próxima distribuição.

( 3.4 Usando a biblioteca Rust Crate rust-decimal

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

) 3.5 Considerar o mecanismo de arredondamento

Na concepção de contratos inteligentes, geralmente adota-se o princípio "Quero tirar proveito, os outros não podem puxar o meu tapete". Dependendo da situação, escolhe-se arredondar para baixo ou para cima, raramente utilizando o arredondamento comum.

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

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
  • 8
  • Compartilhar
Comentário
0/400
SandwichHuntervip
· 07-16 18:07
o pequeno novato de rust está deitado no chão novamente
Ver originalResponder0
WhaleStalkervip
· 07-16 17:00
Os bugs de código são realmente mortais...
Ver originalResponder0
GhostInTheChainvip
· 07-15 16:28
rust ainda precisa preencher decimal... não é simples
Ver originalResponder0
BoredWatchervip
· 07-13 18:40
A biblioteca rust pode escrever contratos?
Ver originalResponder0
GasFeeNightmarevip
· 07-13 18:35
A precisão da operação é tão irritante quanto a minha gás...
Ver originalResponder0
EyeOfTheTokenStormvip
· 07-13 18:34
A taxa de perda de precisão afeta diretamente a relação de lucro e perda, quem ainda usa números de ponto flutuante para quantificação? Os apostadores já puxaram o tapete, certo?
Ver originalResponder0
TokenEconomistvip
· 07-13 18:32
na verdade, a perda de precisão = f(ordem_de_ops, fator_de_escala) ... rust-decimal lib para a vitória
Ver originalResponder0
just_another_walletvip
· 07-13 18:25
Os números de ponto flutuante são muito problemáticos...
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)