Деление/проценты в Solidity (обходной путь)

Деление/проценты являются обязательной частью многих приложений, но их сложно реализовать, поскольку fixedони ufixedеще не поддерживаются в Solidity. Я придумал этот обходной путь:

  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);
    }

  }

Если я хочу получить 12% от 27 и коллировать percent(27, 12), я правильно возвращаюсь 3и 24представляю 3.24. Ограничение этого, конечно, в том, что это percent(12.5, 100)невозможно.

Однако, если я звоню percent(17, 359), я возвращаюсь 61и 3. Однако реальный результат таков 61.03, потому что fractional0 uintперед 3 отбрасывается. Есть ли способ узнать, есть ли у дробной части начальный нуль?

Используя вашу логику, не будет ли любое число <10 иметь начальный ноль для дробной части? В противном случае будет возвращено двузначное число от 10 до 99.
Здесь нет общего решения, потому что количество лидирующих нулей в десятичной части может быть любым. Вам, вероятно, следует немного подумать о том, почему вы установили это требование для начала. Чтобы представить X% из Y с полной точностью , вам нужно сохранить пару чисел — числитель и знаменатель. Точнее - X * Yи 100. Не делите X * Yна, 100пока не дойдете до того момента, когда вам это действительно нужно. Например, предположим, что позже в вашем коде вам нужно умножить это значение на Z, тогда в этот момент вы делаете X * Y * Z / 100.
И, конечно же, если вы также хотите поддерживать дробные проценты, вы можете увеличить 100 до любого разрешения, которого хотите достичь (например, 1000, 10000 и т. д.).
Я думаю, у вас есть решение с указанием разрешения, как описано @goodvibration. Также ваша программа уже дает правильный ответ; как 100/3 = 0.03и100/24 = 0.24

Ответы (2)

На мой взгляд, это, вероятно, неуместное использование логики в цепочке не только из-за сопутствующей неуклюжести кода, но и из-за затрат на газ, связанных с выполнением того, что вызывающий абонент может легко понять для себя.

Сказав это, можно было бы иметь дело с десятичными знаками, используя что-то вроде этого: Невозможно выполнить целочисленное деление

Эта интерпретация возвращает целое число процентов, правильно округленное в большую сторону. Например, 23,5% возвращает 24. Вы можете увеличить точность десятичных дробей, изменив то, как он работает с целочисленными преобразованиями.

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;
    }
}

Надеюсь, поможет.

ОБНОВЛЯТЬ

Чтобы сделать обратное, следующим образом. Кроме того, увеличьте порядок, чтобы вернуть результат с более высокой точностью.

Здесь 12 на 27 (12,27) возвращает 324, что клиент может интерпретировать как 32,4%.

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

Основной вывод — работать с целыми числами. Хитрость с +5и /10заключается в том, чтобы обеспечить правильное округление.

Спасибо, Роб, тестирование вашей функции с использованием, (17, 359)например, вернет 5. Однако я имею в виду создание способа узнать точное целое и дробное число.
Ссылка, на которую вы ссылаетесь, работает аналогично моему коду и имеет ту же проблему; ведущий ноль теряется после сохранения в файле uint.
Я добавил еще один пример.

Как упоминал Роб, работайте с целым числом. Я придумываю другой способ. В моем сценарии я должен хранить процентную ставку с базовой суммой. Чтобы найти процент и добавить к базовой сумме, формула выглядит следующим образом:

y = amount + (amount * percentValue / 100)
y = (100 * amount) + (amount * percentValue) /100
y*100 = amount * (100 + percentValue)

здесь y в основном ожидаемый результат. Я не делю значение на 100, поэтому значение останется целым, а во внешней части я делю значение и сохраняю только вывод

amount(100 + percentValue)

в солидности.