Técnicas y consideraciones de cálculo numérico en contratos inteligentes de Rust

Diario de desarrollo de contratos inteligentes Rust (7): cálculo de valores

Este artículo explorará los problemas de cálculo numérico en la programación de contratos inteligentes en Rust, que incluyen principalmente los problemas de precisión en las operaciones con números de punto flotante, los problemas de precisión en los cálculos con enteros, así como cómo escribir contratos inteligentes en Rust para el cálculo numérico.

1. Problema de precisión en las operaciones con números 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 que son inevitables. Al escribir contratos inteligentes, no se recomienda utilizar operaciones de punto flotante, especialmente 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 la notación científica con una base de 2 para su expresión. Sin embargo, ciertos decimales ( como 0.7) no pueden ser representados con precisión usando un número finito de bits en punto flotante, lo que genera un fenómeno de "redondeo".

Tomando como ejemplo la distribución de 0.7 tokens NEAR a diez usuarios en la cadena pública NEAR, los resultados de los cálculos reales pueden presentar imprecisiones:

ó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}", cantidad); assert_eq!(result_0, 0.07, ""); }

El resultado de la ejecución muestra que el valor de amount no es exactamente 0.7, sino un valor extremadamente cercano de 0.69999999999999995559. Además, el resultado de la operación de división también se vuelve impreciso, siendo 0.06999999999999999 en lugar de 0.07.

Para resolver este problema, se puede considerar el uso de números de punto fijo. En el Protocolo NEAR, generalmente se utiliza 10^24 como denominador, es decir, 10^24 yoctoNEAR equivalen a 1 token NEAR.

El código de prueba modificado es el siguiente:

ó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

Aunque el uso de operaciones enteras puede resolver el problema de pérdida de precisión de los números de punto flotante en ciertos escenarios, los resultados de los cálculos enteros tampoco son completamente precisos y fiables. Algunas de las razones que afectan la precisión de los cálculos enteros incluyen:

2.1 Orden de operaciones

La multiplicación y la división con la misma prioridad aritmética, el cambio en su orden puede afectar directamente el resultado del cálculo. 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,"");

}

Los resultados de la prueba muestran que los resultados de cálculo de result_0 y result_1 son diferentes. Esto se debe a que para la división entera, la precisión menor que el divisor se descartará. Al calcular result_1, (a / b) perderá primero precisión de cálculo y se convertirá en 0; mientras que al calcular result_0, calcular primero a * c puede evitar la pérdida de precisión.

2.2 cantidad demasiado pequeña

Cuando se trata de cálculos de órdenes de magnitud más pequeñas, también pueden surgir problemas 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 de la prueba muestran que los resultados de la operación result_0 y result_1 son diferentes, y que result_1 = 13 está más cerca del valor calculado esperado de 13.3333....

3. Cómo escribir contratos inteligentes de Rust para la valoración numérica

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 una magnitud mayor para crear un numerador más grande. Por ejemplo, se puede expresar 5.123 NEAR como 5.123 * 10^10 = 51_230_000_000.

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

Para los problemas de precisión en cálculos enteros que no se pueden evitar, se puede considerar 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; para i en 1..7 { println!("Round {}", i); offset = distribuir(a_yocto)"10"(, offset(; println!)"Offset {}\n", offset); } }

Este método puede acumular y redistribuir los tokens que no se han distribuido debido a la pérdida de precisión.

( 3.4 Uso de la biblioteca Rust Crate rust-decimal

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

) 3.5 Considerar el mecanismo de redondeo

Al diseñar contratos inteligentes, el problema de redondeo generalmente sigue el principio de "Quiero aprovecharme, los demás no deben sacarme ventaja". Según este principio, si redondear hacia abajo beneficia al contrato, se redondea hacia abajo; si redondear hacia arriba beneficia al contrato, se redondea hacia arriba; el redondeo al más cercano se utiliza muy raramente porque no se puede determinar a quién beneficia.

Al adoptar estos métodos, se puede lograr un cálculo numérico más preciso en los contratos inteligentes de Rust, mejorando la confiabilidad y la equidad del contrato.

![]###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
ZeroRushCaptainvip
· 07-17 09:17
Ah, otra vez comenzando a jugar con esto. La última vez fue un problema de precisión que me llevó a una Posición de bloqueo, perdí tanto que tuve que comer tierra.
Ver originalesResponder0
BlockchainFriesvip
· 07-16 10:52
Los puntos flotantes son una trampa. Quien los use, se echará a perder.
Ver originalesResponder0
MoonBoi42vip
· 07-15 08:48
¿Dónde está la alegría de los números de punto flotante en comparación con los de punto fijo~
Ver originalesResponder0
OnchainDetectivevip
· 07-14 09:54
Es evidente que el error de precisión es precisamente el punto de ruptura que ciertos puentes cross-chain han sido explotados por hackers, es escalofriante.
Ver originalesResponder0
HashRateHermitvip
· 07-14 09:54
El problema de precisión realmente me ha arruinado.
Ver originalesResponder0
GateUser-e87b21eevip
· 07-14 09:47
Otra vez con el tema de los puntos decimales, me tiene preocupado.
Ver originalesResponder0
BearMarketSurvivorvip
· 07-14 09:45
La precisión es una trampa de la que nadie puede escapar, esta persona lo entiende.
Ver originalesResponder0
AirdropLickervip
· 07-14 09:35
El redondeo de números de punto flotante es un tema muy complicado, ya he caído en esta trampa antes.
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)