Na programação de contratos inteligentes, a precisão dos cálculos numéricos é especialmente importante. Este artigo explorará os problemas comuns de cálculo numérico em contratos inteligentes Rust e suas soluções.
1. Problema 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 lidar com taxas ou juros que envolvem decisões econômicas/financeiras importantes, não é recomendável utilizar operações com números de ponto flutuante.
O tipo de ponto flutuante de dupla precisão f64 na linguagem Rust segue o padrão IEEE 754 e utiliza 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 fixo, resultando em fenômenos de "arredondamento".
Por exemplo, ao distribuir 0.7 tokens NEAR na blockchain NEAR para 10 usuários:
ferrugem
let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
o valor real de amount é 0.69999999999999995559, o resultado de result_0 é 0.06999999999999999, e não o esperado 0.07.
Para resolver este problema, pode-se considerar o uso da representação de números fixos. No Protocolo NEAR, normalmente usa-se 10^24 como denominador, ou seja, 1 NEAR = 10^24 yoctoNEAR. A forma de cálculo modificada é a seguinte:
ferrugem
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;
Desta forma, é possível obter resultados de cálculo precisos: 0,7 NEAR / 10 = 0,07 NEAR.
2. Problema da precisão no cálculo de inteiros em Rust
Embora o uso de cálculos inteiros possa resolver problemas de precisão de ponto flutuante em certos cenários, ainda existem alguns fatores que afetam a precisão dos cálculos.
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 a divisão inteira descarta a precisão inferior ao divisor.
2.2 quantidade muito pequena
Quando se trata de cálculos em escalas menores, isso também pode levar a problemas 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, e result_1 está mais próximo do valor esperado real.
3. Como escrever contratos inteligentes de cálculo numérico em Rust
Para aumentar a precisão dos cálculos numéricos em contratos inteligentes Rust, podem ser tomadas as seguintes medidas:
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
Usar uma magnitude maior para criar moléculas maiores e aumentar a precisão dos cálculos.
3.3 perda de precisão nos cálculos acumulados
Para os problemas de precisão de cálculo inteiro que não podem ser evitados, pode-se considerar registrar a perda de precisão acumulada 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;
let recorded_offset = token_to_distribute - per_user_share * USER_NUM;
recorded_offset
}
Este método pode compensar gradualmente a perda de precisão em múltiplas distribuições.
( 3.4 Usando a biblioteca Rust Crate rust-decimal
Esta biblioteca é adequada para cálculos financeiros decimais que requerem precisão eficaz e não apresentam erro de arredondamento.
) 3.5 Considerar o mecanismo de arredondamento
Ao projetar contratos inteligentes, os problemas de arredondamento geralmente seguem o princípio "Quero me beneficiar, os outros não podem me explorar". Escolha arredondar para baixo, arredondar para cima ou arredondar para o mais próximo, conforme a situação.
Ao adotar esses métodos, é possível aumentar significativamente a precisão e a confiabilidade dos cálculos numéricos em contratos inteligentes Rust.
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
7 Curtidas
Recompensa
7
3
Compartilhar
Comentário
0/400
MaticHoleFiller
· 17h atrás
Passar o dia preso em problemas de precisão e perder esforços em vão.
Ver originalResponder0
StablecoinAnxiety
· 17h atrás
É um pouco complicado ter que lidar com algoritmos e finanças ao mesmo tempo~
Ver originalResponder0
GhostAddressMiner
· 17h atrás
Mais um caso típico de transferir a responsabilidade de um problema de precisão para o padrão, sem saber quanto dinheiro foi liquidado em segredo, os dados na cadeia não mentem.
Rust contratos inteligentes numéricos: guia de prevenção de armadilhas e melhores práticas
Rust contratos inteligentes中的数值精算
Na programação de contratos inteligentes, a precisão dos cálculos numéricos é especialmente importante. Este artigo explorará os problemas comuns de cálculo numérico em contratos inteligentes Rust e suas soluções.
1. Problema 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 lidar com taxas ou juros que envolvem decisões econômicas/financeiras importantes, não é recomendável utilizar operações com números de ponto flutuante.
O tipo de ponto flutuante de dupla precisão f64 na linguagem Rust segue o padrão IEEE 754 e utiliza 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 fixo, resultando em fenômenos de "arredondamento".
Por exemplo, ao distribuir 0.7 tokens NEAR na blockchain NEAR para 10 usuários:
ferrugem let amount: f64 = 0.7;
let divisor: f64 = 10.0; let result_0 = amount / divisor;
o valor real de amount é 0.69999999999999995559, o resultado de result_0 é 0.06999999999999999, e não o esperado 0.07.
Para resolver este problema, pode-se considerar o uso da representação de números fixos. No Protocolo NEAR, normalmente usa-se 10^24 como denominador, ou seja, 1 NEAR = 10^24 yoctoNEAR. A forma de cálculo modificada é a seguinte:
ferrugem 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;
Desta forma, é possível obter resultados de cálculo precisos: 0,7 NEAR / 10 = 0,07 NEAR.
2. Problema da precisão no cálculo de inteiros em Rust
Embora o uso de cálculos inteiros possa resolver problemas de precisão de ponto flutuante em certos cenários, ainda existem alguns fatores que afetam a precisão dos cálculos.
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 a divisão inteira descarta a precisão inferior ao divisor.
2.2 quantidade muito pequena
Quando se trata de cálculos em escalas menores, isso também pode levar a problemas 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, e result_1 está mais próximo do valor esperado real.
3. Como escrever contratos inteligentes de cálculo numérico em Rust
Para aumentar a precisão dos cálculos numéricos em contratos inteligentes Rust, podem ser tomadas as seguintes medidas:
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
Usar uma magnitude maior para criar moléculas maiores e aumentar a precisão dos cálculos.
3.3 perda de precisão nos cálculos acumulados
Para os problemas de precisão de cálculo inteiro que não podem ser evitados, pode-se considerar registrar a perda de precisão acumulada 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; let recorded_offset = token_to_distribute - per_user_share * USER_NUM; recorded_offset }
Este método pode compensar gradualmente a perda de precisão em múltiplas distribuições.
( 3.4 Usando a biblioteca Rust Crate rust-decimal
Esta biblioteca é adequada para cálculos financeiros decimais que requerem precisão eficaz e não apresentam erro de arredondamento.
) 3.5 Considerar o mecanismo de arredondamento
Ao projetar contratos inteligentes, os problemas de arredondamento geralmente seguem o princípio "Quero me beneficiar, os outros não podem me explorar". Escolha arredondar para baixo, arredondar para cima ou arredondar para o mais próximo, conforme a situação.
Ao adotar esses métodos, é possível aumentar significativamente a precisão e a confiabilidade dos cálculos numéricos em contratos inteligentes Rust.
![]###https://img-cdn.gateio.im/webp-social/moments-6e8b4081214a69423fc7ae022d05c728.webp###