Desarrollo de contratos inteligentes en Rust: superando el problema de precisión en cálculos numéricos

Problemas de precisión en los cálculos numéricos en el desarrollo de contratos inteligentes en Rust

1. Problemas de precisión en operaciones de punto flotante

El lenguaje Rust admite de forma nativa operaciones de punto flotante, pero estas operaciones presentan problemas de precisión de cálculo inevitables. Al escribir contratos inteligentes, no se recomienda el uso de operaciones de punto flotante, especialmente al manejar tasas o intereses que impliquen 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 y utiliza la notación científica con una base de 2. Algunos decimales no se pueden representar con precisión utilizando un número finito de bits de punto flotante, lo que da lugar a fenómenos de "redondeo".

Por ejemplo, al distribuir 0.7 tokens NEAR a 10 usuarios en la cadena pública NEAR:

óxido #[test] fn precision_test_float() { let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
println!("El valor de la cantidad: {:.20}", amount); assert_eq!(result_0, 0.07, ""); }

El resultado de salida muestra que el valor de amount es 0.69999999999999995559, no el exacto 0.7. El resultado de la operación de división también se convierte en 0.06999999999999999, en lugar del esperado 0.07.

Para resolver este problema, se puede considerar el uso de números de punto fijo. En el NEAR Protocol, se utiliza comúnmente 10^24 yoctoNEAR para representar 1 token NEAR. Código de prueba modificado:

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

Así se puede obtener un resultado de cálculo preciso: 0.7 NEAR / 10 = 0.07 NEAR.

2. Problemas de precisión en los cálculos enteros de Rust

El uso de operaciones enteras puede resolver el problema de pérdida de precisión en operaciones de punto flotante en ciertos escenarios, pero los resultados de los cálculos enteros tampoco son completamente precisos y confiables.

2.1 Orden de operaciones

El cambio en el orden de multiplicación y división con la misma prioridad aritmética puede afectar directamente el resultado del cálculo, lo que lleva a problemas de precisión en los cálculos enteros. Por ejemplo:

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

}

El resultado de la ejecución muestra que result_0 y result_1 no son iguales. La razón es que la división entera descarta la precisión menor que el divisor. Al calcular result_1, (a / b) pierde primero precisión y se convierte en 0; mientras que al calcular result_0, primero se calcula a * c para evitar la pérdida de precisión.

2.2 cantidad demasiado pequeña

Cuando se trata de cálculos decimales, las operaciones enteras pueden provocar una pérdida de precisión:

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

}

Los resultados muestran result_0=12, result_1=13, mientras que el valor esperado real debería ser 13.3333....

3. Cómo escribir contratos inteligentes de cálculo actuarial numérico en Rust

Para mejorar la precisión, se pueden tomar las siguientes medidas de protección:

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

Usar un orden de magnitud mayor para crear un numerador más grande. Por ejemplo, expresar 5.123 NEAR como 5.123 * 10^10 = 51_230_000_000 para participar en cálculos posteriores.

3.3 Pérdida de precisión en los cálculos acumulativos

Para los problemas de precisión inevitables, se puede registrar la pérdida acumulada de precisión 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; 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; for i in 1..7 { println!("Round {}", i); offset = distribute(to_yocto)"10"(, offset(; println!)"Offset {}\n", offset); } }

Este método puede acumular los tokens restantes de cada distribución y entregarlos juntos en la próxima distribución, con el objetivo final de alcanzar la entrega completa.

( 3.4 Uso de la biblioteca Rust Crate rust-decimal

Esta biblioteca es adecuada para cálculos financieros decimales que requieren una precisión efectiva y no tienen errores de redondeo.

) 3.5 Considerar el mecanismo de redondeo

En el diseño de contratos inteligentes, el problema de redondeo generalmente se aborda con el principio de "quiero beneficiarme, los demás no deben aprovecharse de mí". Según la situación, se elige redondear hacia abajo o hacia arriba, raramente se utiliza el redondeo estándar.

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

Ver originales
Esta página puede contener contenido de terceros, que se proporciona únicamente con fines informativos (sin garantías ni declaraciones) y no debe considerarse como un respaldo por parte de Gate a las opiniones expresadas ni como asesoramiento financiero o profesional. Consulte el Descargo de responsabilidad para obtener más detalles.
  • Recompensa
  • 8
  • Compartir
Comentar
0/400
rugpull_survivorvip
· 07-14 15:34
El problema de redondeo ha afectado a muchas personas.
Ver originalesResponder0
WhaleWatchervip
· 07-14 07:46
El control de precisión es clave.
Ver originalesResponder0
consensus_failurevip
· 07-13 12:52
Usar una precisión fija es más seguro
Ver originalesResponder0
Ser_Liquidatedvip
· 07-12 19:41
Si hay otro problema de precisión, se obtendrá liquidación.
Ver originalesResponder0
DecentralizeMevip
· 07-11 16:13
Usa números de punto fijo.
Ver originalesResponder0
BoredWatchervip
· 07-11 15:58
El código es un poco confuso.
Ver originalesResponder0
BrokenDAOvip
· 07-11 15:53
La sobrecapacidad total de potencia computacional es un problema.
Ver originalesResponder0
MEVSandwichMakervip
· 07-11 15:51
Es un viejo pozo, se recomienda evitarlo.
Ver originalesResponder0
Opere con criptomonedas en cualquier momento y lugar
qrCode
Escanee para descargar la aplicación Gate
Comunidad
Español
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)