Розробка смартконтрактів на Rust: подолання проблеми точності числових обчислень

Проблема точності обчислень у розробці смартконтрактів на Rust

1. Проблема точності обчислень з плаваючою комою

Мова Rust нативно підтримує обчислення з плаваючою комою, але обчислення з плаваючою комою мають невідворотні проблеми з точністю. При написанні смартконтрактів не рекомендується використовувати обчислення з плаваючою комою, особливо при обробці важливих економічних/фінансових рішень, таких як співвідношення або відсоткові ставки.

Тип подвоєної точності f64 у мові Rust відповідає стандарту IEEE 754 і використовує наукову нотацію з основою 2. Деякі десяткові числа не можуть бути точно представлені обмеженою кількістю бітів з плаваючою комою, що призводить до "округлення".

Наприклад, при розподілі 0.7 NEAR токенів 10 користувачам на блокчейні NEAR:

іржа #[test] fn precision_test_float() { Нехай кількість: f64 = 0,7;
Нехай дільник: f64 = 10,0;
нехай result_0 = сума / дільник;
println!("Значення суми: {:.20}", amount); assert_eq!(result_0, 0,07, "); }

Результат виводу показує, що значення amount дорівнює 0.69999999999999995559, а не точному 0.7. Результат ділення також став неточним 0.06999999999999999, а не очікуваним 0.07.

Для вирішення цієї проблеми можна розглянути можливість використання фіксованих точок. У NEAR Protocol зазвичай використовують 10^24 yoctoNEAR для представлення 1 токена NEAR. Змінений тестовий код:

іржа #[test] fn precision_test_integer() { нехай N: u128 = 1_000_000_000_000_000_000_000;
Нехай кількість: U128 = 700_000_000_000_000_000; Нехай дільник: u128 = 10;
нехай result_0 = сума / дільник; assert_eq!(result_0, 70_000_000_000_000_000_000, ""); }

Таким чином можна отримати точний результат обчислення: 0,7 NEAR / 10 = 0,07 NEAR.

!

2. Проблема точності обчислень з цілими числами в Rust

Використання цілочисельних обчислень може вирішити проблему втрати точності при обчисленнях з плаваючою комою в деяких сценаріях, але результати цілочисельних обчислень також не є абсолютно точними та надійними.

2.1 порядок операцій

У множенні та діленні з однаковим пріоритетом арифметичних дій зміна їх порядку може безпосередньо вплинути на результат обчислень, що призводить до проблем з точністю обчислень цілих чисел. Наприклад:

ржавий #[test] fn precision_test_div_before_mul() { Нехай a: u128 = 1_0000; нехай b: u128 = 10_0000; Нехай С: U128 = 20;

let result_0 = a.checked_mul(c).expect("ERR_MUL").checked_ div019283746574839201b(.expect)"ERR_DIV"(;
нехай result_1 = a.checked_div)b(.expect)"ERR_DIV"(.checked_ MUL019283746574839201c).expect("ERR_MUL");

assert_eq!(result_0,result_1,");

}

Виконання показало, що result_0 і result_1 не рівні. Причина в тому, що цілочисельне ділення відкидає точність, меншу за дільник. При обчисленні result_1, (a / b) спочатку втрачає точність і стає 0; тоді як при обчисленні result_0 спочатку обчислюється a * c, що запобігає втраті точності.

( 2.2 надто малий масштаб

Коли мова йде про обчислення з дробами, цілісні операції можуть призводити до втрати точності:

іржа #) fn precision_test_decimals###[test] { Нехай А: U128 = 10; нехай b: u128 = 3; Нехай c: u128 = 4; Нехай десятковий дріб: u128 = 100_0000;

let result_0 = a.checked_div(b).expect("ERR_DIV").checked_ MUL019283746574839201c(.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_ div019283746574839201decimal).expect("ERR_DIV");

println!("{}:{}", result_0, result_1);
assert_eq!(result_0, result_1, ");

}

Результати показують result_0=12, result_1=13, а фактичне очікуване значення має бути 13.3333....

!

3. Як написати чисельні актуарні смартконтракти на Rust

Для підвищення точності можна вжити такі заходи захисту:

( 3.1 Налаштування порядку виконання операцій

Нехай множення цілих чисел має пріоритет над діленням цілих чисел.

) 3.2 збільшити порядок цілого числа

Використовуйте більші порядки, щоб створити більші молекули. Наприклад, вираження 5.123 NEAR може бути представлене як 5.123 * 10^10 = 51_230_000_000 для подальших обчислень.

( 3.3 Втрата точності обчислень накопичення

Щодо незворотних проблем з точністю, можна зафіксувати накопичені втрати точності обчислень. Наприклад:

іржа const USER_NUM: u128 = 3;

FN distribute)amount: U128, зміщення: u128### -> u128 { Нехай token_to_distribute = зсув + сума; Нехай per_user_share = token_to_distribute / USER_NUM; println!###"per_user_share {}", per_user_share###; нехай recorded_offset = token_to_distribute - per_user_share * USER_NUM; зафіксований_зсув }

#( fn record_offset_test)( { нехай mut зміщення: u128 = 0; для i в 1..7 { println!)"Round {}", i[test]; зміщення = distribute(to_yocto)"10"(, offset); println!("Offset {}\n", offset(; } }

Цей метод дозволяє накопичувати залишкові токени після кожного розподілу та випускати їх разом під час наступного розподілу, врешті-решт досягаючи мети повного розподілу.

) 3.4 Використання бібліотеки Rust Crate rust-decimal

Ця бібліотека підходить для фінансових розрахунків з десятковими числами, які вимагають точної обчислювальної точності та не мають похибки округлення.

) 3.5 Розгляньте механізм округлення

У дизайні смартконтрактів проблема округлення зазвичай використовується за принципом "Я хочу отримати вигоду, інші не повинні користуватися моїми ресурсами". Вибір між округленням до меншого або більшого значення залежить від ситуації, рідко використовується правило округлення.

!

Переглянути оригінал
Ця сторінка може містити контент третіх осіб, який надається виключно в інформаційних цілях (не в якості запевнень/гарантій) і не повинен розглядатися як схвалення його поглядів компанією Gate, а також як фінансова або професійна консультація. Див. Застереження для отримання детальної інформації.
  • Нагородити
  • 8
  • Поділіться
Прокоментувати
0/400
rugpull_survivorvip
· 07-14 15:34
Кілька людей потрапили в пастку округлення
Переглянути оригіналвідповісти на0
WhaleWatchervip
· 07-14 07:46
Контроль точності є дуже важливим
Переглянути оригіналвідповісти на0
consensus_failurevip
· 07-13 12:52
Використання фіксованої точності є більш надійним
Переглянути оригіналвідповісти на0
Ser_Liquidatedvip
· 07-12 19:41
Якщо знову виникнуть проблеми з точністю, обов'язково ліквідуватися.
Переглянути оригіналвідповісти на0
DecentralizeMevip
· 07-11 16:13
Замість цього використовуйте фіксовану точку.
Переглянути оригіналвідповісти на0
BoredWatchervip
· 07-11 15:58
Код трохи заплутаний.
Переглянути оригіналвідповісти на0
BrokenDAOvip
· 07-11 15:53
Обчислювальна потужність загального переповнення буде проблемою.
Переглянути оригіналвідповісти на0
MEVSandwichMakervip
· 07-11 15:51
Це стара яма, рекомендую обійти.
Переглянути оригіналвідповісти на0
  • Закріпити