[Ethereum] Division/Percentages in Solidity (a workaround)

contract-debuggingcontract-designcontract-developmentSecuritysolidity

Division/percentages is a required part of many applications but is difficult to implement since fixed and ufixed are not supported yet in Solidity. I've come up with this workaround:

  pragma solidity ^0.4.9;

  contract Math {

    function Math() public {}

    function percent(uint a, uint b) public constant returns(uint, uint) {
      uint c = a*b;
      uint integer = c/100;
      uint fractional = c%100;
      return (integer, fractional);
    }

  }

If I want to get 12% of 27 and call percent(27, 12), I correctly get back 3 and 24 representing 3.24. The limitation to this of course is that percent(12.5, 100) isn't possible.

However if I call percent(17, 359), I get back 61 and 3. The real result is 61.03, however, because fractional is a uint, the 0 in front of the 3 is dropped. Is there a way to find out if a fractional has a leading zero?

Best Answer

In my opinion this is probably an inappropriate use of on-chain logic, not only because of the attendant awkwardness of the code, but also because of the gas cost involved in doing something the caller can easily figure out for themselves.

Having said that, one could deal with decimal places using something like this: Can't do any integer division

This interpretation returns an integer percentage properly rounded up. For example, 23.5% returns 24. You could increase the decimals of precision by tinkering with the way it plays with the integer conversions.

pragma solidity 0.4.24;

contract Percent {

    function getPercent(uint part, uint whole) public pure returns(uint percent) {
        uint numerator = part * 1000;
        require(numerator > part); // overflow. Should use SafeMath throughout if this was a real implementation. 
        uint temp = numerator / whole + 5; // proper rounding up
        return temp / 10;
    }
}

Hope it helps.

UPDATE

To do the reverse, as follows. Also, increase the order of magnitude to return a result with higher precision.

Here, 12 over 27 (12,27) returns 324 which the client can interpret as 32.4%.

function getFraction(uint percent, uint base) public pure returns(uint portion) {
    uint temp = percent * base * 10 + 5;
    return temp / 10;
}

The main takeaway is to work with integers. The trickery with +5 and /10 is to ensure correct rounding up.

Related Topic