Проблема точности вычислений целых чисел в смарт-контрактах 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_000;
let amount: u128 = 700_000_000_000_000_000_000_000; пусть делитель: u128 = 10;
пусть result_0 = сумма / делитель; assert_eq!(result_0, 70_000_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; пусть c: 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() { пусть a: 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 Потеря точности при накоплении вычислений

Записывать и накапливать потери точности, компенсировать в последующих вычислениях:

ржавчина константа 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() { let mut offset: u128 = 0; для i в 1..7 { смещение = distribute(10_000_000_000_000_000_000_000_000, offset); } }

!

3.4 Использование библиотеки Rust Crate rust-decimal

Библиотека подходит для финансовых расчетов с высокоточной десятичной арифметикой без ошибок округления.

3.5 Учитывайте механизм округления

В дизайне смарт-контрактов округление обычно следует принципу "мне это выгодно": если округление вниз мне выгодно, то округляем вниз, если округление вверх мне выгодно, то округляем вверх, редко используется округление до ближайшего.

!

EQ0.82%
Посмотреть Оригинал
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
  • Награда
  • 8
  • Поделиться
комментарий
0/400
LostBetweenChainsvip
· 07-25 17:53
Не могу справиться с целыми числами, на каких контрактах играть?
Посмотреть ОригиналОтветить0
WagmiWarriorvip
· 07-25 11:45
Уже вижу 7-ю статью, так тонко, так тонко.
Посмотреть ОригиналОтветить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... держите ваши флоаты плотно или получите rekt, сер
Посмотреть ОригиналОтветить0
TrustlessMaximalistvip
· 07-22 22:16
Эти плавающие вычисления нужно избегать.
Посмотреть ОригиналОтветить0
  • Закрепить