在具体写代码的时候,会发现一个问题:即如何合适的表达小数?一种简单的思路是把小数都乘以10^18,但是需要考虑到会不会溢出,以及后续还需要一个除法才行。
在具体写代码的时候,会发现一个问题:即如何合适的表达小数?一种简单的思路是把小数都乘以10^18,但是需要考虑到会不会溢出,以及后续还需要一个除法才行。
这里看一下compound中是如何对待小数乘法的:
compound中将每一个小数按照精度分为两种:EXP和Double。
对于Exp,compound定义了一个结构体:
uint constant expScale = 1e18;
struct Exp{
uint mantissa;
}
对于Double,compound定义了一个结构体:
uint constant doubldScale = 1e36;
struct Double{
uint mantissa;
}
在compound里面用的比较多的是Exp精度的数值,这里着重以Exp来分析小数如何加减乘除:
首先是对于EXP这一结构体的分拆:
function truncate(Exp memory exp) pure internal returns (uint) {
return exp.mantissa.div(expScale);
}
然后是关于Exp的大小比较:
function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa.lt(right.mantissa);
}
然后是Exp的乘法,这里需要考虑Exp乘以Exp,Exp乘以普通数值,返回结果都是Exp类型
//Exp乘以Exp
function mul_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: a.mantissa.mul(b.mantissa).div(expScale)});
}
//Exp乘以数值
function mul_(Exp memory a, uint b) pure internal returns (Exp memory) {
return Exp({mantissa: a.mantissa.mul(b)});
}
然后是Exp的除法,这里需要考虑Exp除以Exp,Exp除以普通数值,返回结果都是Exp类型
//Exp除以Exp
//除法的时候要先乘在除,防止精度丢失
function div_(Exp memory a, Exp memroy b) pure internal returns (Exp memory) {
return Exp({mantissa: a.mantissa.mul(expScale).div(b.mantissa)});
}
function div_(Exp memory a, uint b) pure internal returns (Exp memory) {
return Exp({mantissa: a.mantissa.div(b)});
}
Exp的加法,减法与之类似,不再赘述。
在本次compound的设计中,为保持简单,就使用compound自己提供的计算小数的函数,不再改编。
这里的关键问题是:
全局变量borrowIndex应该存储的是放大1e18次方之后的数据,还是原始数据呢?
这里应该存储放大后的数据
mantissa的含义应该是单精度数据,并不是认为将它放大10^18次方,而只是认为提高他的精度到10^18次方。
对于:cToken相关的totalSupply, totalBorrows, totalReserves其精度已经就是18位了,就不需要再包装一层。而对于borrowIndex其精度需要提升到18位。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!