pragma solidity ^0.5.16; import "./SignedSafeMath.sol"; // TODO: Test suite // https://docs.synthetix.io/contracts/SignedSafeDecimalMath library SignedSafeDecimalMath { using SignedSafeMath for int; /* Number of decimal places in the representations. */ uint8 public constant decimals = 18; uint8 public constant highPrecisionDecimals = 27; /* The number representing 1.0. */ int public constant UNIT = int(10**uint(decimals)); /* The number representing 1.0 for higher fidelity numbers. */ int public constant PRECISE_UNIT = int(10**uint(highPrecisionDecimals)); int private constant UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR = int(10**uint(highPrecisionDecimals - decimals)); /** * @return Provides an interface to UNIT. */ function unit() external pure returns (int) { return UNIT; } /** * @return Provides an interface to PRECISE_UNIT. */ function preciseUnit() external pure returns (int) { return PRECISE_UNIT; } /** * @dev Rounds an input with an extra zero of precision, returning the result without the extra zero. * Half increments round away from zero; positive numbers at a half increment are rounded up, * while negative such numbers are rounded down. This behaviour is designed to be consistent with the * unsigned version of this library (SafeDecimalMath). */ function _roundDividingByTen(int valueTimesTen) private pure returns (int) { int increment; if (valueTimesTen % 10 >= 5) { increment = 10; } else if (valueTimesTen % 10 <= -5) { increment = -10; } return (valueTimesTen + increment) / 10; } /** * @return The result of multiplying x and y, interpreting the operands as fixed-point * decimals. * * @dev A unit factor is divided out after the product of x and y is evaluated, * so that product must be less than 2**256. As this is an integer division, * the internal division always rounds down. This helps save on gas. Rounding * is more expensive on gas. */ function multiplyDecimal(int x, int y) internal pure returns (int) { /* Divide by UNIT to remove the extra factor introduced by the product. */ return x.mul(y) / UNIT; } /** * @return The result of safely multiplying x and y, interpreting the operands * as fixed-point decimals of the specified precision unit. * * @dev The operands should be in the form of a the specified unit factor which will be * divided out after the product of x and y is evaluated, so that product must be * less than 2**256. * * Unlike multiplyDecimal, this function rounds the result to the nearest increment. * Rounding is useful when you need to retain fidelity for small decimal numbers * (eg. small fractions or percentages). */ function _multiplyDecimalRound( int x, int y, int precisionUnit ) private pure returns (int) { /* Divide by UNIT to remove the extra factor introduced by the product. */ int quotientTimesTen = x.mul(y) / (precisionUnit / 10); return _roundDividingByTen(quotientTimesTen); } /** * @return The result of safely multiplying x and y, interpreting the operands * as fixed-point decimals of a precise unit. * * @dev The operands should be in the precise unit factor which will be * divided out after the product of x and y is evaluated, so that product must be * less than 2**256. * * Unlike multiplyDecimal, this function rounds the result to the nearest increment. * Rounding is useful when you need to retain fidelity for small decimal numbers * (eg. small fractions or percentages). */ function multiplyDecimalRoundPrecise(int x, int y) internal pure returns (int) { return _multiplyDecimalRound(x, y, PRECISE_UNIT); } /** * @return The result of safely multiplying x and y, interpreting the operands * as fixed-point decimals of a standard unit. * * @dev The operands should be in the standard unit factor which will be * divided out after the product of x and y is evaluated, so that product must be * less than 2**256. * * Unlike multiplyDecimal, this function rounds the result to the nearest increment. * Rounding is useful when you need to retain fidelity for small decimal numbers * (eg. small fractions or percentages). */ function multiplyDecimalRound(int x, int y) internal pure returns (int) { return _multiplyDecimalRound(x, y, UNIT); } /** * @return The result of safely dividing x and y. The return value is a high * precision decimal. * * @dev y is divided after the product of x and the standard precision unit * is evaluated, so the product of x and UNIT must be less than 2**256. As * this is an integer division, the result is always rounded down. * This helps save on gas. Rounding is more expensive on gas. */ function divideDecimal(int x, int y) internal pure returns (int) { /* Reintroduce the UNIT factor that will be divided out by y. */ return x.mul(UNIT).div(y); } /** * @return The result of safely dividing x and y. The return value is as a rounded * decimal in the precision unit specified in the parameter. * * @dev y is divided after the product of x and the specified precision unit * is evaluated, so the product of x and the specified precision unit must * be less than 2**256. The result is rounded to the nearest increment. */ function _divideDecimalRound( int x, int y, int precisionUnit ) private pure returns (int) { int resultTimesTen = x.mul(precisionUnit * 10).div(y); return _roundDividingByTen(resultTimesTen); } /** * @return The result of safely dividing x and y. The return value is as a rounded * standard precision decimal. * * @dev y is divided after the product of x and the standard precision unit * is evaluated, so the product of x and the standard precision unit must * be less than 2**256. The result is rounded to the nearest increment. */ function divideDecimalRound(int x, int y) internal pure returns (int) { return _divideDecimalRound(x, y, UNIT); } /** * @return The result of safely dividing x and y. The return value is as a rounded * high precision decimal. * * @dev y is divided after the product of x and the high precision unit * is evaluated, so the product of x and the high precision unit must * be less than 2**256. The result is rounded to the nearest increment. */ function divideDecimalRoundPrecise(int x, int y) internal pure returns (int) { return _divideDecimalRound(x, y, PRECISE_UNIT); } /** * @dev Convert a standard decimal representation to a high precision one. */ function decimalToPreciseDecimal(int i) internal pure returns (int) { return i.mul(UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR); } /** * @dev Convert a high precision decimal to a standard decimal representation. */ function preciseDecimalToDecimal(int i) internal pure returns (int) { int quotientTimesTen = i / (UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR / 10); return _roundDividingByTen(quotientTimesTen); } }