Проблема точності обчислень цілих чисел у смартконтрактах Rust та рішення

Rust смартконтракти养成日记(7):цілісні обчислення точності проблеми

Огляд минулих подій:

  • Rust смартконтракти養成日記(1) статус даних контракту визначення та реалізація методів
  • Щоденник розвитку смартконтрактів Rust (2) Написання модульних тестів смартконтрактів Rust
  • Щоденник розвитку Rust смартконтрактів (3) Розгортання Rust смартконтрактів, виклик функцій та використання Explorer
  • Rust смартконтракти养成日记(4)Rust смартконтракти整数溢出
  • Rust смартконтракти养成日记(5)重入攻击
  • Rust смартконтракти виховання щоденник (6) відмова в обслуговуванні атака

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

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

Rust дотримується стандарту IEEE 754 для представлення чисел з плаваючою комою. Тип f64 з подвійною точністю представляється в комп'ютері у вигляді двійкового наукового запису.

Деякі дроби можуть бути точно представлені у вигляді обмеженої кількості двійкових розрядів, наприклад, 0.8125 може бути представлене як 0.1101. Але дроби, такі як 0.7, створюють безкінечне циклічне двійкове представлення, яке не може бути точно представлене обмеженою кількістю плаваючої коми, що призводить до проблеми "округлення".

У прикладі розподілу 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, result_0 становить 0.06999999999999999, що не дорівнює очікуваному 0.07.

Щоб вирішити цю проблему, можна використовувати фіксовану точку. У NEAR зазвичай 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).unwrap().checked_div(b). unwrap();
let result_1 = a.checked_div(b).unwrap().checked_mul(c). unwrap();

assert_eq!(result_0,result_1);

}

Результати показують result_0 = 2, result_1 = 0.

Причина в тому, що цілочисельне ділення відкидає точність, меншу за дільник. При обчисленні 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).unwrap().checked_mul(c). unwrap();

let result_1 = a.checked_mul(decimal).unwrap()
                .checked_div(b).unwrap()
                .checked_mul(c).unwrap()
                .checked_div(decimal).unwrap();

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 збільшити порядок цілого числа

Використовуйте більший порядок величини, щоб створити більші молекули. Наприклад, визначте 1 NEAR = 10^24 yoctoNEAR.

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(10_000_000_000_000_000_000_000_000, offset); } }

!

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

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

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

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

!

EQ2.03%
Переглянути оригінал
Ця сторінка може містити контент третіх осіб, який надається виключно в інформаційних цілях (не в якості запевнень/гарантій) і не повинен розглядатися як схвалення його поглядів компанією Gate, а також як фінансова або професійна консультація. Див. Застереження для отримання детальної інформації.
  • Нагородити
  • 8
  • Поділіться
Прокоментувати
0/400
LostBetweenChainsvip
· 07-25 17:53
Цілісні обчислення не можуть бути вирішені, що за контракт.
Переглянути оригіналвідповісти на0
WagmiWarriorvip
· 07-25 11:45
Бачу сьому статтю, тонка, тонка.
Переглянути оригіналвідповісти на0
MetaMisfitvip
· 07-25 01:13
Rust ці смартконтракти ям так багато, правда?
Переглянути оригіналвідповісти на0
DuckFluffvip
· 07-22 22:44
Ого, знову з плаваючими числами трапилася якась справа~
Переглянути оригіналвідповісти на0
MaticHoleFillervip
· 07-22 22:44
Ті, хто потрапив у пастку точності, приходять поділитися досвідом!
Переглянути оригіналвідповісти на0
MetadataExplorervip
· 07-22 22:44
Ця частина часто ігнорується, але вона досить важлива.
Переглянути оригіналвідповісти на0
MEVHuntervip
· 07-22 22:29
точність є MEV honeypot... тримайте свої флоати щільно, або вас знищать, сер
Переглянути оригіналвідповісти на0
TrustlessMaximalistvip
· 07-22 22:16
Цю проблему з плаваючою точкою потрібно уникати.
Переглянути оригіналвідповісти на0
  • Закріпити