Percentage Calculation in Solidity – Using SafeMath for Calculating Percentages

safemath

I hope this question is not asked, but i googled too much and tried to find out the problem my self.

I'm trying to get a dynamic percentage using SafeMath

uint256 lowNumber = 316442921;
uint256 highNumber = 632885842;
uint256 distanceFromHigherNumber = 544386085;

In daily math i found a way to calculate that but it won't work here.

result = ((distanceFromHigherNumber - lowNumber) / (highNumber - lowNumber)) * 100;
percentage = 100 - result // in percentage should be ~ 28%

In solidity i did it this way:

uint256 result = (distanceFromHigherNumber.sub(lowNumber) / highNumber.sub(lowNumber)) * 100;
uint256 percentage = 100 - result;

It returns always 100, i guess it is maybe returning 1 instead of 0.72 and when i'm multiplying it by 100, when i play around it a little bit i get only zero or 100.

Any help please, i'm too new to solidity.

Best Answer

Solidity has no concept of floating-point numbers, so you need to work on an integer representation of your number.

Your idea is good, but the order of operations won't work on integers.

result = ((x - low) / (high - low))

Will always return a number between 0 and 1 by definition (unless you allow x < low or x > high but it wouldn't make any sense). Solidity only deals with integers, so it will return either 0 or 1, nothing in between. When you multiply this value by 100, you get either 0 or 100...

You need to multiply before doing the division, to immediately generate a number between 0 and 100 (or more depending on the precision that you are looking for).

This code returns the percentage that you are looking for when given 544386085 as parameter : 72.

pragma solidity ^0.7.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.4/contracts/math/SafeMath.sol";

    contract Percentage {
    
        using SafeMath for uint256;
        
        uint256 high = 632885842;  
        uint256 low =  316442921;
        uint256 precision = 2;
    
        function getPercentage(uint256 x) public view returns (uint256) {
            
            require(x >= low, "Number too low");
            require(x <= high, "Number too high");
            
            return x.sub(low).mul(10 ** precision) / high.sub(low);
        }
    }

Btw, SafeMath is superfluous here, since if you know that x >= low and x <= high (due to the require statements) there is no overflow / undeflow possible.