Проблема точності числових операцій у смартконтрактах Rust та рішення для її оптимізації

robot
Генерація анотацій у процесі

Щоденник розвитку смартконтрактів на Rust (7) Проблема точності операцій з плаваючими числами та цілими числами

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

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

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

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

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

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

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

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

іржа #[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_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,");

}

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

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

Масштаб занадто малий також може призвести до проблем з точністю:

іржа #[test] fn precision_test_decimals() { Нехай А: U128 = 10; нехай b: u128 = 3; Нехай c: u128 = 4; Нехай десятковий дріб: 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");

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; нехай recorded_offset = token_to_distribute - per_user_share * USER_NUM; записаний_зсув }

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

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

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

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

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

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

!

Переглянути оригінал
Ця сторінка може містити контент третіх осіб, який надається виключно в інформаційних цілях (не в якості запевнень/гарантій) і не повинен розглядатися як схвалення його поглядів компанією Gate, а також як фінансова або професійна консультація. Див. Застереження для отримання детальної інформації.
  • Нагородити
  • 8
  • Поділіться
Прокоментувати
0/400
SandwichHuntervip
· 07-16 18:07
rust малий пташеня знову ліг на землю
Переглянути оригіналвідповісти на0
WhaleStalkervip
· 07-16 17:00
Помилка в коді справді вбиває...
Переглянути оригіналвідповісти на0
GhostInTheChainvip
· 07-15 16:28
rust ще потрібно заповнити десяткові... не просто
Переглянути оригіналвідповісти на0
BoredWatchervip
· 07-13 18:40
Чи може бібліотека rust писати контракти?
Переглянути оригіналвідповісти на0
GasFeeNightmarevip
· 07-13 18:35
Точність обчислень так само набридлива, як і мій газовий збір...
Переглянути оригіналвідповісти на0
EyeOfTheTokenStormvip
· 07-13 18:34
Втрата точності безпосередньо впливає на співвідношення прибутку до збитку, хто ще використовує плаваючу точку для квантової торгівлі? Гравці, мабуть, вже втікли.
Переглянути оригіналвідповісти на0
TokenEconomistvip
· 07-13 18:32
насправді, втрата точності = f(ордер_операцій, масштабний_фактор) ... бібліотека rust-decimal для перемоги
Переглянути оригіналвідповісти на0
just_another_walletvip
· 07-13 18:25
Плаваюча точка занадто підступна...
Переглянути оригіналвідповісти на0
  • Закріпити