📢 Gate广场 #MBG任务挑战# 发帖赢大奖活动火热开启!
想要瓜分1,000枚MBG?现在就来参与,展示你的洞察与实操,成为MBG推广达人!
💰️ 本期将评选出20位优质发帖用户,每人可轻松获得50枚MBG!
如何参与:
1️⃣ 调研MBG项目
对MBG的基本面、社区治理、发展目标、代币经济模型等方面进行研究,分享你对项目的深度研究。
2️⃣ 参与并分享真实体验
参与MBG相关活动(包括CandyDrop、Launchpool或现货交易),并晒出你的参与截图、收益图或实用教程。可以是收益展示、简明易懂的新手攻略、小窍门,也可以是现货行情点位分析,内容详实优先。
3️⃣ 鼓励带新互动
如果你的帖子吸引到他人参与活动,或者有好友评论“已参与/已交易”,将大幅提升你的获奖概率!
MBG热门活动(帖文需附下列活动链接):
Gate第287期Launchpool:MBG — 质押ETH、MBG即可免费瓜分112,500 MBG,每小时领取奖励!参与攻略见公告:https://www.gate.com/announcements/article/46230
Gate CandyDrop第55期:CandyDrop x MBG — 通过首次交易、交易MBG、邀请好友注册交易即可分187,500 MBG!参与攻略见公告:https://www.gate.com/announcements
Rust智能合约整数计算精度问题及解决方案
Rust智能合约养成日记(7):整数计算精度问题
往期回顾:
1. 浮点数运算的精度问题
与Solidity不同,Rust原生支持浮点数运算。但浮点数运算存在不可避免的精度问题,因此不推荐在智能合约中使用,尤其是处理重要经济/金融决策的比率或利率时。
Rust遵循IEEE 754标准表示浮点数。f64双精度浮点类型在计算机内部采用二进制科学计数法表示。
一些小数可以用有限位数的二进制精确表示,如0.8125可表示为0.1101。但0.7这样的小数会产生无限循环的二进制表示,无法用有限位浮点数准确表示,存在"舍入"问题。
在NEAR公链上分发0.7个NEAR代币给10位用户的例子中:
rust #[test] fn precision_test_float() { let amount: f64 = 0.7;
let divisor: f64 = 10.0;
let result_0 = amount / divisor;
println!("The value of amount: {:.20}", amount); assert_eq!(result_0, 0.07); }
运行结果显示amount实际值为0.69999999999999995559,result_0为0.06999999999999999,不等于预期的0.07。
为解决这个问题,可以使用定点数。在NEAR中,通常用10^24个yoctoNEAR表示1个NEAR代币。修改后的代码:
rust
#[test] fn precision_test_integer() { let N: u128 = 1_000_000_000_000_000_000_000_000;
let amount: u128 = 700_000_000_000_000_000_000_000; let divisor: u128 = 10;
let result_0 = amount / divisor; assert_eq!(result_0, 70_000_000_000_000_000_000_000); }
这样可以得到精确的结果:0.7 NEAR / 10 = 0.07 NEAR。
2. Rust整数计算精度的问题
虽然整数运算可解决某些场景下的浮点数精度问题,但整数计算也存在精度问题。
2.1 运算顺序
同级别的乘除法,顺序变化可能影响结果:
rust #[test] fn precision_test_div_before_mul() { let a: u128 = 1_0000; let b: u128 = 10_0000; let c: u128 = 20;
}
结果显示result_0 = 2, result_1 = 0。
原因是整数除法会舍弃小于除数的精度。计算result_1时,(a / b)先失去精度变为0;而result_0先计算(a * c)避免了精度丢失。
2.2 过小的数量级
rust #[test] fn precision_test_decimals() { let a: u128 = 10; let b: u128 = 3; let c: u128 = 4; let decimal: u128 = 100_0000;
}
结果显示result_0 = 12, result_1 = 13,后者更接近实际值13.3333。
3. 如何编写数值精算的Rust智能合约
为提高精度,可采取以下措施:
3.1 调整运算的操作顺序
让整数乘法优先于除法。
3.2 增加整数的数量级
使用更大的数量级,创造更大的分子。如定义1 NEAR = 10^24 yoctoNEAR。
3.3 积累运算精度的损失
记录并累计精度损失,在后续运算中补偿:
rust const USER_NUM: u128 = 3;
fn distribute(amount: u128, offset: u128) -> u128 { let token_to_distribute = offset + amount; let per_user_share = token_to_distribute / USER_NUM; let recorded_offset = token_to_distribute - per_user_share * USER_NUM; recorded_offset }
#[test] fn record_offset_test() { let mut offset: u128 = 0; for i in 1..7 { offset = distribute(10_000_000_000_000_000_000_000_000, offset); } }
3.4 使用Rust Crate库rust-decimal
该库适用于需要高精度且无舍入误差的小数金融计算。
3.5 考虑舍入机制
智能合约设计中,舍入通常遵循"对我有利"原则:向下取整对我有利则向下,向上取整对我有利则向上,很少采用四舍五入。