以太坊Solidity中的除法

17

我正在创建一份发行代币的合约。我希望持有代币的账户能够查询他们所拥有的代币与所有已发行代币的百分比。我知道以太坊尚未实现浮点数。我该怎么办?

2个回答

24

最好(燃气成本低且易于实现)在客户端执行该计算,而不是在Solidity中执行。如果您发现需要在Solidity中执行该操作,那么只需要通过移动小数点来使用整数进行计算,类似于:https://en.wikipedia.org/wiki/Parts-per_notation

例如,此函数使您可以决定精度级别,并使用一个额外的精度级别以正确四舍五入:

pragma solidity ^0.4.6;

contract Divide {

  function percent(uint numerator, uint denominator, uint precision) public 

  constant returns(uint quotient) {

         // caution, check safe-to-multiply here
        uint _numerator  = numerator * 10 ** (precision+1);
        // with rounding of last digit
        uint _quotient =  ((_numerator / denominator) + 5) / 10;
        return ( _quotient);
  }

}

如果你输入101,450和3,你将得到224,即22.4%。

希望这能帮助到您。


这将用于计算合同能够为给定代币持有者支付多少。例如,如果代币持有者拥有20个代币,而总共有100个代币。合同需要能够决定20个代币相当于合同中总以太币的20%。例如,总以太币可能为5 ETH。我可以分Wei吗?这该怎么做? - GK1
好的,不错。Wei是最小的单位,所以您必须决定如何处理余数。如果您要完全精确,那么请使用更高精度的用户余额作为状态变量来跟踪分数Wei...在累积余额足够大时进行偶尔结算。请记住,实际移动单个Wei单位将需要燃气,因此请考虑移动小金额的效率。 - Rob Hitchens
1
如果传递的数值是20,100,3,那么你会得到200 - 我们称之为“部分”,意思是每千份中的200份,或者20.0%。因此,你可以拥有任意所需的精度。然后,你可以说amountToDivide * portion / 1000。1,000等于3(原始精度)的10次方。我纠正了函数中的一个疏忽,使其正确地四舍五入。顺便说一下,“转换为十进制”是一个我在金融圈听到的简略说法...意思是乘以100。没什么特别的。另外,“safe-to”的注释是针对你处理可能导致溢出和产生不良结果的大数值情况。 - Rob Hitchens
在什么情况下乘法不安全? - GK1
2
溢出。如果数字对于uint256来说太大了,它不会抛出异常...它只会丢弃高位比特并返回一个小数。为什么在文档中看到类似if(a + b <a)throw; 的示例。需要认真思考这种情况。例如,if(分子* 10 *(精度+1)<分子)throw; ...在我确定该检查之前,我会再仔细考虑一下。希望传达了这个想法。 - Rob Hitchens
显示剩余3条评论

0
你可以使用二进制小数点或固定数字表示法。固定点数表示法介绍例如

 11010.1 in base2 = 1 * 24 + 1 * 23 + 0 * 22 + 1 * 21 + 0* 20 + 1 * 2-1 = 26.5

百分比是通过计算得出的

  // x is the percentage
  a/b = x/100 => x= (100*a)/b

如果要计算大数的除法,您可以使用FixidityLib.sol库。

function divide(Fixidity storage fixidity, int256 a, int256 b) public view returns (int256) {
    if(b == fixidity.fixed_1) return a;
    assert(b != 0);
    return multiply(fixidity, a, reciprocal(fixidity, b));
}

有太多数学库或合约,每个实现除法操作的方式都不同:

FloatMath.sol

DSMath 合约

abdk-libraries-solidity

openzeppelin-contracts


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接