Разработка смарт-контрактов на Rust: преодоление проблемы точности числовых вычислений

Проблема точности вычислений в числах при разработке смарт-контрактов на Rust

1. Проблема точности вычислений с плавающей запятой

Язык Rust изначально поддерживает операции с плавающей запятой, но операции с плавающей запятой имеют неизбежные проблемы с точностью вычислений. При написании смарт-контрактов не рекомендуется использовать операции с плавающей запятой, особенно при обработке коэффициентов или процентных ставок, связанных с важными экономическими/финансовыми решениями.

В языке Rust тип двойной точности с плавающей запятой f64 соответствует стандарту 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 обычно 10^24 yoctoNEAR используется для обозначения 1 токена NEAR. Измененный тестовый код:

ржавчина #[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_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;

Пусть result_0 = a.checked_mul(c).expect("ERR_MUL").checked_ div(b).expect0192837374656574839201"ERR_DIV"(;
Пусть result_1 = a.checked_div)b(.expect)"ERR_DIV"(.checked_ мул)c(.expect0192837374656574839201"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] { пусть a: u128 = 10; пусть b: u128 = 3; пусть c: u128 = 4; пусть десятичная: u128 = 100_0000;

Пусть result_0 = a.checked_div(b).expect("ERR_DIV").checked_ мул(c).expect0192837374656574839201"ERR_MUL"(;
Пусть 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, ""(;

}

Результаты показывают result_0=12, result_1=13, тогда как фактическое ожидаемое значение должно быть 13.3333....

! [])https://img-cdn.gateio.im/webp-social/moments-1933a4a2dd723a847f0059d31d1780d1.webp(

3. Как написать смарт-контракты на Rust для числовой актуарии

Для повышения точности можно принять следующие меры защиты:

) 3.1 Изменение порядка выполнения операций

Позвольте целочисленному умножению иметь приоритет над целочисленным делением.

( 3.2 Увеличение порядка целых чисел

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

) 3.3 Потеря точности накопительных вычислений

Для неизбежных проблем с точностью можно зафиксировать накопленные потери вычислительной точности. Например:

ржавчина константа 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; recorded_offset }

#) FN record_offset_test() { let mut offset: u128 = 0; для i в 1..7 { println![test]"Раунд {}", i(; offset = distribute)to_yocto("10"), offset(; println!("Offset {}\n", offset); } }

Этот метод позволяет накапливать оставшиеся токены после каждого распределения и выплачивать их вместе при следующем распределении, в конечном итоге достигая цели полного распределения.

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

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

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

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

! [])https://img-cdn.gateio.im/webp-social/moments-6e8b4081214a69423fc7ae022d05c728.webp###

Посмотреть Оригинал
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании 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
  • Закрепить