Проблема точности вычислений в числах при разработке смарт-контрактов на Rust
1. Проблема точности вычислений с плавающей запятой
Язык Rust изначально поддерживает операции с плавающей запятой, но операции с плавающей запятой имеют неизбежные проблемы с точностью вычислений. При написании смарт-контрактов не рекомендуется использовать операции с плавающей запятой, особенно при обработке коэффициентов или процентных ставок, связанных с важными экономическими/финансовыми решениями.
В языке Rust тип двойной точности с плавающей запятой f64 соответствует стандарту IEEE 754 и использует научную нотацию с основанием 2. Некоторые десятичные числа не могут быть точно представлены с помощью конечной длины плавающей точки, что приводит к явлению "округления".
Например, при распределении 0.7 токена NEAR между 10 пользователями на блокчейне NEAR:
Результат вывода показывает, что значение amount равно 0.69999999999999995559, а не точному 0.7. Результат деления также стал неточным 0.06999999999999999, а не ожидаемым 0.07.
Чтобы решить эту проблему, можно рассмотреть использование фиксированной точки. В протоколе NEAR обычно 10^24 yoctoNEAR используется для обозначения 1 токена NEAR. Измененный тестовый код:
Таким образом, можно получить точный результат вычислений: 0.7 NEAR / 10 = 0.07 NEAR.
!
2. Проблема точности вычислений с целыми числами в Rust
Использование целочисленных вычислений может решить проблему потери точности в вычислениях с плавающей запятой в некоторых сценариях, но результаты целочисленных вычислений также не являются полностью точными и надежными.
2.1 Порядок операций
При одинаковом приоритете арифметических операций, порядок выполнения умножения и деления может напрямую повлиять на результат вычислений, что приводит к проблемам с точностью целочисленных вычислений. Например:
Результат выполнения показывает, что result_0 и result_1 не равны. Причина в том, что целочисленное деление отбрасывает точность, меньшую чем делитель. При вычислении result_1, (a / b) сначала теряет точность и становится 0; в то время как при вычислении result_0 сначала выполняется a * c, что предотвращает потерю точности.
( 2.2 слишком маленький масштаб
При выполнении вычислений с плавающей запятой операции с целыми числами могут привести к потере точности:
3. Как написать смарт-контракты на Rust для числовой актуарии
Для повышения точности можно принять следующие меры защиты:
) 3.1 Изменение порядка выполнения операций
Позвольте целочисленному умножению иметь приоритет над целочисленным делением.
( 3.2 Увеличение порядка целых чисел
Используйте более крупные порядки, чтобы создать большие молекулы. Например, представление 5.123 NEAR может быть записано как 5.123 * 10^10 = 51_230_000_000 для использования в последующих вычислениях.
) 3.3 Потеря точности накопительных вычислений
Для неизбежных проблем с точностью можно зафиксировать накопленные потери вычислительной точности. Например:
#)
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 Рассмотреть механизм округления
В проектировании смарт-контрактов проблема округления обычно решается по принципу "Я хочу получить выгоду, другим нельзя забирать мои средства". В зависимости от ситуации выбирается округление вниз или округление вверх, очень редко используется округление до ближайшего.
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
10 Лайков
Награда
10
8
Поделиться
комментарий
0/400
rugpull_survivor
· 07-14 15:34
Много людей попалось на ловушку округления.
Посмотреть ОригиналОтветить0
WhaleWatcher
· 07-14 07:46
Контроль точности очень важен
Посмотреть ОригиналОтветить0
consensus_failure
· 07-13 12:52
Используйте фиксированную точность для большей надежности
Посмотреть ОригиналОтветить0
Ser_Liquidated
· 07-12 19:41
Если снова возникнет проблема с точностью, произойдет ликвидация.
Разработка смарт-контрактов на 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 и 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=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###