Cálculo numérico en contratos inteligentes de Rust
En la programación de contratos inteligentes, el problema de la precisión en los cálculos numéricos es especialmente importante. Este artículo explorará los problemas comunes de cálculo numérico en contratos inteligentes de Rust y sus soluciones.
1. Problemas de precisión en las operaciones de punto flotante
El lenguaje Rust admite operaciones de punto flotante de forma nativa, pero estas operaciones presentan problemas inevitables de precisión en los cálculos. No se recomienda el uso de operaciones de punto flotante al tratar con tasas o intereses que implican decisiones económicas/financieras importantes.
El tipo de punto flotante de doble precisión f64 en el lenguaje Rust sigue el estándar IEEE 754, utilizando notación científica con una base de 2. Algunos decimales ( como 0.7) no se pueden representar con precisión con un número de punto flotante de longitud finita, lo que puede dar lugar a un fenómeno de "redondeo".
Por ejemplo, al distribuir 0.7 tokens NEAR en la cadena de bloques NEAR a 10 usuarios:
óxido
let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
El valor real de amount es 0.69999999999999995559, el resultado de result_0 es 0.06999999999999999, en lugar del esperado 0.07.
Para resolver este problema, se puede considerar el uso de la representación de punto fijo. En el Protocolo NEAR, generalmente se utiliza 10^24 como denominador, es decir, 1 NEAR = 10^24 yoctoNEAR. La forma de cálculo modificada es la siguiente:
óxido
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;
De esta manera se puede obtener un resultado de cálculo preciso: 0.7 NEAR / 10 = 0.07 NEAR.
2. Problemas de precisión en cálculos enteros de Rust
El uso de cálculos enteros puede resolver problemas de precisión de números de punto flotante en ciertos escenarios, pero aún existen algunos factores que afectan la precisión de los cálculos.
2.1 Orden de operaciones
La multiplicación y la división con la misma prioridad aritmética, el cambio en el orden de aparición puede afectar directamente el resultado del cálculo. Por ejemplo:
óxido
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");
Los resultados de result_0 y result_1 son diferentes, la razón es que la división de enteros descartará la precisión menor que el divisor.
2.2 cantidad demasiado pequeña
Cuando se trata de cálculos de menor magnitud, también puede dar lugar a problemas de precisión:
óxido
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");
El resultado de result_0 y result_1 es diferente, y result_1 está más cerca del valor esperado real.
3. Cómo escribir contratos inteligentes de cálculo numérico en Rust
Para mejorar la precisión en los cálculos numéricos en los contratos inteligentes de Rust, se pueden tomar las siguientes medidas:
3.1 Ajustar el orden de las operaciones
Hacer que la multiplicación de enteros tenga prioridad sobre la división de enteros.
3.2 aumentar el orden de magnitud de los enteros
Utilizar un mayor orden de magnitud para crear moléculas más grandes y mejorar la precisión de los cálculos.
3.3 Pérdida de precisión en las operaciones de acumulación
Para los problemas de precisión de cálculo de enteros que son inevitables, se puede considerar registrar la pérdida de precisión acumulada en los cálculos. Por ejemplo:
óxido
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 puede compensar gradualmente la pérdida de precisión en múltiples rondas de distribución.
( 3.4 Uso de la biblioteca Rust Crate rust-decimal
Esta biblioteca es adecuada para cálculos financieros de decimales que requieren una precisión eficaz y no tienen errores de redondeo.
) 3.5 Considerar el mecanismo de redondeo
Al diseñar contratos inteligentes, el problema de redondeo suele seguir el principio de "quiero beneficiarme, los demás no deben aprovecharse de mí". Seleccione redondear hacia abajo, redondear hacia arriba o redondear al número entero más cercano según la situación.
Al adoptar estos métodos, se puede mejorar significativamente la precisión y la fiabilidad de los cálculos numéricos en contratos inteligentes de 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 me gusta
Recompensa
7
3
Compartir
Comentar
0/400
MaticHoleFiller
· hace17h
Pasar todo el día atrapado en problemas de precisión y perder el esfuerzo en vano.
Ver originalesResponder0
StablecoinAnxiety
· hace17h
Es un poco abrumador tener que lidiar tanto con algoritmos como con finanzas~
Ver originalesResponder0
GhostAddressMiner
· hace18h
Otro caso típico de desviar un problema de precisión hacia el estándar, sin saber cuántos fondos se han retirado en secreto, los datos on-chain no mienten.
Cálculo preciso de valores en contratos inteligentes Rust: Guía para evitar trampas y mejores prácticas
Cálculo numérico en contratos inteligentes de Rust
En la programación de contratos inteligentes, el problema de la precisión en los cálculos numéricos es especialmente importante. Este artículo explorará los problemas comunes de cálculo numérico en contratos inteligentes de Rust y sus soluciones.
1. Problemas de precisión en las operaciones de punto flotante
El lenguaje Rust admite operaciones de punto flotante de forma nativa, pero estas operaciones presentan problemas inevitables de precisión en los cálculos. No se recomienda el uso de operaciones de punto flotante al tratar con tasas o intereses que implican decisiones económicas/financieras importantes.
El tipo de punto flotante de doble precisión f64 en el lenguaje Rust sigue el estándar IEEE 754, utilizando notación científica con una base de 2. Algunos decimales ( como 0.7) no se pueden representar con precisión con un número de punto flotante de longitud finita, lo que puede dar lugar a un fenómeno de "redondeo".
Por ejemplo, al distribuir 0.7 tokens NEAR en la cadena de bloques NEAR a 10 usuarios:
óxido let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
El valor real de amount es 0.69999999999999995559, el resultado de result_0 es 0.06999999999999999, en lugar del esperado 0.07.
Para resolver este problema, se puede considerar el uso de la representación de punto fijo. En el Protocolo NEAR, generalmente se utiliza 10^24 como denominador, es decir, 1 NEAR = 10^24 yoctoNEAR. La forma de cálculo modificada es la siguiente:
óxido 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;
De esta manera se puede obtener un resultado de cálculo preciso: 0.7 NEAR / 10 = 0.07 NEAR.
2. Problemas de precisión en cálculos enteros de Rust
El uso de cálculos enteros puede resolver problemas de precisión de números de punto flotante en ciertos escenarios, pero aún existen algunos factores que afectan la precisión de los cálculos.
2.1 Orden de operaciones
La multiplicación y la división con la misma prioridad aritmética, el cambio en el orden de aparición puede afectar directamente el resultado del cálculo. Por ejemplo:
óxido 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");
Los resultados de result_0 y result_1 son diferentes, la razón es que la división de enteros descartará la precisión menor que el divisor.
2.2 cantidad demasiado pequeña
Cuando se trata de cálculos de menor magnitud, también puede dar lugar a problemas de precisión:
óxido 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");
El resultado de result_0 y result_1 es diferente, y result_1 está más cerca del valor esperado real.
3. Cómo escribir contratos inteligentes de cálculo numérico en Rust
Para mejorar la precisión en los cálculos numéricos en los contratos inteligentes de Rust, se pueden tomar las siguientes medidas:
3.1 Ajustar el orden de las operaciones
Hacer que la multiplicación de enteros tenga prioridad sobre la división de enteros.
3.2 aumentar el orden de magnitud de los enteros
Utilizar un mayor orden de magnitud para crear moléculas más grandes y mejorar la precisión de los cálculos.
3.3 Pérdida de precisión en las operaciones de acumulación
Para los problemas de precisión de cálculo de enteros que son inevitables, se puede considerar registrar la pérdida de precisión acumulada en los cálculos. Por ejemplo:
óxido 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 puede compensar gradualmente la pérdida de precisión en múltiples rondas de distribución.
( 3.4 Uso de la biblioteca Rust Crate rust-decimal
Esta biblioteca es adecuada para cálculos financieros de decimales que requieren una precisión eficaz y no tienen errores de redondeo.
) 3.5 Considerar el mecanismo de redondeo
Al diseñar contratos inteligentes, el problema de redondeo suele seguir el principio de "quiero beneficiarme, los demás no deben aprovecharse de mí". Seleccione redondear hacia abajo, redondear hacia arriba o redondear al número entero más cercano según la situación.
Al adoptar estos métodos, se puede mejorar significativamente la precisión y la fiabilidad de los cálculos numéricos en contratos inteligentes de Rust.
![]###https://img-cdn.gateio.im/webp-social/moments-6e8b4081214a69423fc7ae022d05c728.webp###