Dividir con decimales en Solidity

Solidity no soporta decimales, pero al igual que en la vida real muchas veces vamos a necesitar realizar cálculos que requieren resultados con decimales.

Para poder realizar operaciones con decimales lo que recomiendo es multiplicar por 100 la cantidad previa a operar (en el caso de que queramos dos decimales) y una vez hagamos nuestra operación (por ejemplo una división) convertir el resultado a string para agregarle la coma donde corresponda.

El código habla sólo, así que os voy a poner aquí un ejemplo con Solidity 0.4.17 de cómo hacerlo.

pragma solidity ^0.4.17;

contract DivisionConDecimales{
    
    function dividir(uint numberA, uint numberB) public view returns(string){
        
        uint result;
        
        numberA = numberA * 100; // Le agregamos 2 ceros para poder luego reducir a 2 decimales
        
        result = numberA / numberB;
        
        bytes32 resultBytes = uintToBytes(result);
        
        string memory resultString = bytes32ToString(resultBytes);
        
        string memory decimals = substring(resultString,2,4);
        
        resultString = substring(resultString,0,utfStringLength(resultString)-2);
        
        resultString = strConcat(resultString,",",decimals,"","");
        
        return resultString;
        
    }
    
    function bytes32ToString(bytes32 x) private pure returns (string) {
        bytes memory bytesString = new bytes(32);
        uint charCount = 0;
        for (uint j = 0; j < 32; j++) {
            byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
            if (char != 0) {
                bytesString[charCount] = char;
                charCount++;
            }
        }
        bytes memory bytesStringTrimmed = new bytes(charCount);
        for (j = 0; j < charCount; j++) {
            bytesStringTrimmed[j] = bytesString[j];
        }
        
        return string(bytesStringTrimmed);
    }
    
    function uintToBytes(uint v) private pure returns (bytes32 ret) {
        if (v == 0) {
            ret = '0';
        }
        else {
            while (v > 0) {
                ret = bytes32(uint(ret) / (2 ** 8));
                ret |= bytes32(((v % 10) + 48) * 2 ** (8 * 31));
                v /= 10;
            }
        }
        return ret;
    }
    
    function substring(string str, uint startIndex, uint endIndex) private pure returns (string) {
        bytes memory strBytes = bytes(str);
        bytes memory result = new bytes(endIndex-startIndex);
        for(uint i = startIndex; i < endIndex; i++) {
            result[i-startIndex] = strBytes[i];
        }
        return string(result);
    }
    
    function strConcat(string _a, string _b, string _c, string _d, string _e) internal returns (string){
        bytes memory _ba = bytes(_a);
        bytes memory _bb = bytes(_b);
        bytes memory _bc = bytes(_c);
        bytes memory _bd = bytes(_d);
        bytes memory _be = bytes(_e);
        string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length);
        bytes memory babcde = bytes(abcde);
        uint k = 0;
        
        for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i];
        
        for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i];
        for (i = 0; i < _bc.length; i++) babcde[k++] = _bc[i];
        for (i = 0; i < _bd.length; i++) babcde[k++] = _bd[i];
        for (i = 0; i < _be.length; i++) babcde[k++] = _be[i];
        
        return string(babcde);
    }
    
    function strConcat(string _a, string _b, string _c, string _d) internal returns (string) {
        return strConcat(_a, _b, _c, _d, "");
    }
    
    function strConcat(string _a, string _b, string _c) internal returns (string) {
        return strConcat(_a, _b, _c, "", "");
    }
    
    function strConcat(string _a, string _b) internal returns (string) {
        return strConcat(_a, _b, "", "", "");
    }
    
    function utfStringLength(string str) internal
    returns (uint length)
    {
        uint i=0;
        bytes memory string_rep = bytes(str);

        while (i>7==0)
                i+=1;
            else if (string_rep[i]>>5==0x6)
                i+=2;
            else if (string_rep[i]>>4==0xE)
                i+=3;
            else if (string_rep[i]>>3==0x1E)
                i+=4;
            else
                //For safety
                i+=1;

            length++;
        }
    }
    
}

Tengo que encontrar como compartir mejor el código en Blogger, queda un poco raro :).

Para poder probar el código podéis utilizar Remix.

Comentarios