В данной статье будет рассмотрена проблема числового актуария в программировании смарт-контрактов на Rust, включая проблемы точности операций с плавающей запятой, точности целочисленных вычислений, а также то, как написать смарт-контракт Rust для числового актаария и другие связанные темы.
1. Проблема точности операций с плавающей запятой
Язык Rust изначально поддерживает операции с плавающей запятой, но операции с плавающей запятой имеют неизбежные проблемы с вычислительной точностью. При написании смарт-контрактов не рекомендуется использовать операции с плавающей запятой, особенно при обработке коэффициентов или процентных ставок, связанных с важными экономическими/финансовыми решениями.
В языке Rust тип с плавающей точкой двойной точности f64 соответствует стандарту IEEE 754 и использует научную нотацию с основанием 2 для представления. Однако некоторые десятичные числа (, такие как 0.7), не могут быть точно представлены с помощью конечной длины плавающей точки и могут возникать "округления".
В качестве примера распределения 0,7 токена NEAR среди десяти пользователей на блокчейне NEAR, фактические результаты расчетов могут быть неточными:
Результат выполнения показывает, что значение amount не равно точному 0.7, а является крайне близким значением 0.69999999999999995559. Дальнейший результат деления также становится неточным 0.06999999999999999, а не ожидаемым 0.07.
Чтобы решить эту проблему, можно рассмотреть использование фиксированной точки. В протоколе NEAR обычно используется 10^24 в качестве знаменателя, то есть 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 слишком маленький порядок
При вычислениях с меньшими порядками величины могут возникать проблемы с точностью:
Результаты тестирования показывают, что результаты вычислений result_0 и result_1 различны, и result_1 = 13 ближе к фактическому ожидаемому значению вычислений 13.3333....
!
3. Как написать смарт-контракты на Rust для числовых расчетов
Для повышения точности можно предпринять следующие меры защиты:
3.1 Изменение порядка выполнения операций
Заставьте целочисленное умножение иметь приоритет над целочисленным делением.
3.2 увеличение порядков целых чисел
Используйте более крупные порядки, чтобы создать большие молекулы. Например, 5.123 NEAR можно выразить как 5.123 * 10^10 = 51_230_000_000.
3.3 Потеря точности вычислений при накоплении
Для неизбежной проблемы точности целочисленных вычислений можно рассмотреть возможность учета накопленных потерь точности вычислений. Например:
#[test]
FN record_offset_test() {
let mut offset: u128 = 0;
для i в 1..7 {
println!("Round {}", i);
offset = distribute(to_yocto("10"), offset);
println!("Offset {}\n", offset);
}
}
Этот метод может накапливать и перераспределять токены, которые не были распределены из-за потерь точности.
3.4 Использование библиотеки Rust Crate rust-decimal
Библиотека подходит для финансовых расчетов с плавающей запятой, которые требуют точных вычислений и не имеют ошибок округления.
3.5 Учитывайте механизм округления
При проектировании смарт-контрактов проблема округления обычно следует принципу "Я хочу получить выгоду, другие не должны забирать мой урожай". Согласно этому принципу, если округление вниз выгодно для контракта, то производится округление вниз; если округление вверх выгодно для контракта, то производится округление вверх; округление до ближайшего целого редко используется, так как невозможно определить, кому это будет выгодно.
Используя эти методы, можно добиться более точных числовых вычислений в смарт-контрактах на Rust, повысив надежность и справедливость контракта.
!
Посмотреть Оригинал
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
14 Лайков
Награда
14
8
Поделиться
комментарий
0/400
ZeroRushCaptain
· 07-17 09:17
Ах, опять начали мучить эту штуку. В прошлый раз из-за проблемы с точностью меня наказали по закрытой позиции, убытки такие, что пришлось есть землю.
Посмотреть ОригиналОтветить0
BlockchainFries
· 07-16 10:52
Плавающая точка - это такая беда, кто использует, тот и попадает в неприятности.
Посмотреть ОригиналОтветить0
MoonBoi42
· 07-15 08:48
Где же точность плавающей точки так хороша, как у фиксированной?~
Посмотреть ОригиналОтветить0
OnchainDetective
· 07-14 09:54
Очевидно, что ошибка точности является той уязвимостью, которую некоторые кроссчейн мосты используют Хакеры, это внушает ужас.
Посмотреть ОригиналОтветить0
HashRateHermit
· 07-14 09:54
Проблема с точностью действительно меня загнала в угол.
Посмотреть ОригиналОтветить0
GateUser-e87b21ee
· 07-14 09:47
Снова проблемы с плавающей точкой, голова кругом.
Посмотреть ОригиналОтветить0
BearMarketSurvivor
· 07-14 09:45
Эта яма с точностью никому не избежать, этот человек понимает.
Посмотреть ОригиналОтветить0
AirdropLicker
· 07-14 09:35
Округление чисел с плавающей запятой - это действительно глубокая яма, я уже туда попал.
Техники и рекомендации по точности чисел в смарт-контрактах Rust
Rust смарт-контракты养成日记(7):数值精算
В данной статье будет рассмотрена проблема числового актуария в программировании смарт-контрактов на Rust, включая проблемы точности операций с плавающей запятой, точности целочисленных вычислений, а также то, как написать смарт-контракт Rust для числового актаария и другие связанные темы.
1. Проблема точности операций с плавающей запятой
Язык Rust изначально поддерживает операции с плавающей запятой, но операции с плавающей запятой имеют неизбежные проблемы с вычислительной точностью. При написании смарт-контрактов не рекомендуется использовать операции с плавающей запятой, особенно при обработке коэффициентов или процентных ставок, связанных с важными экономическими/финансовыми решениями.
В языке Rust тип с плавающей точкой двойной точности f64 соответствует стандарту IEEE 754 и использует научную нотацию с основанием 2 для представления. Однако некоторые десятичные числа (, такие как 0.7), не могут быть точно представлены с помощью конечной длины плавающей точки и могут возникать "округления".
В качестве примера распределения 0,7 токена NEAR среди десяти пользователей на блокчейне 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.7, а является крайне близким значением 0.69999999999999995559. Дальнейший результат деления также становится неточным 0.06999999999999999, а не ожидаемым 0.07.
Чтобы решить эту проблему, можно рассмотреть использование фиксированной точки. В протоколе NEAR обычно используется 10^24 в качестве знаменателя, то есть 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 слишком маленький порядок
При вычислениях с меньшими порядками величины могут возникать проблемы с точностью:
ржавчина #[test] fn precision_test_decimals() { пусть a: u128 = 10; пусть b: u128 = 3; пусть c: u128 = 4; пусть десятичная: u128 = 100_0000;
}
Результаты тестирования показывают, что результаты вычислений result_0 и result_1 различны, и result_1 = 13 ближе к фактическому ожидаемому значению вычислений 13.3333....
!
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; записанное смещение }
#[test] FN record_offset_test() { let mut offset: u128 = 0; для i в 1..7 { println!("Round {}", i); offset = distribute(to_yocto("10"), offset); println!("Offset {}\n", offset); } }
Этот метод может накапливать и перераспределять токены, которые не были распределены из-за потерь точности.
3.4 Использование библиотеки Rust Crate rust-decimal
Библиотека подходит для финансовых расчетов с плавающей запятой, которые требуют точных вычислений и не имеют ошибок округления.
3.5 Учитывайте механизм округления
При проектировании смарт-контрактов проблема округления обычно следует принципу "Я хочу получить выгоду, другие не должны забирать мой урожай". Согласно этому принципу, если округление вниз выгодно для контракта, то производится округление вниз; если округление вверх выгодно для контракта, то производится округление вверх; округление до ближайшего целого редко используется, так как невозможно определить, кому это будет выгодно.
Используя эти методы, можно добиться более точных числовых вычислений в смарт-контрактах на Rust, повысив надежность и справедливость контракта.
!