Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
175 lines (145 sloc) 6.15 KB
pragma solidity ^0.4.24;
import "./ErrorReporter.sol";
import "./CarefulMath.sol";
/**
* @title Exponential module for storing fixed-decision decimals
* @author Compound
* @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
* Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
* `Exp({mantissa: 5100000000000000000})`.
*/
contract Exponential is ErrorReporter, CarefulMath {
// TODO: We may wish to put the result of 10**18 here instead of the expression.
// Per https://solidity.readthedocs.io/en/latest/contracts.html#constant-state-variables
// the optimizer MAY replace the expression 10**18 with its calculated value.
uint constant expScale = 10**18;
// See TODO on expScale
uint constant halfExpScale = expScale/2;
struct Exp {
uint mantissa;
}
uint constant mantissaOne = 10**18;
uint constant mantissaOneTenth = 10**17;
/**
* @dev Creates an exponential from numerator and denominator values.
* Note: Returns an error if (`num` * 10e18) > MAX_INT,
* or if `denom` is zero.
*/
function getExp(uint num, uint denom) pure internal returns (Error, Exp memory) {
(Error err0, uint scaledNumerator) = mul(num, expScale);
if (err0 != Error.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
(Error err1, uint rational) = div(scaledNumerator, denom);
if (err1 != Error.NO_ERROR) {
return (err1, Exp({mantissa: 0}));
}
return (Error.NO_ERROR, Exp({mantissa: rational}));
}
/**
* @dev Adds two exponentials, returning a new exponential.
*/
function addExp(Exp memory a, Exp memory b) pure internal returns (Error, Exp memory) {
(Error error, uint result) = add(a.mantissa, b.mantissa);
return (error, Exp({mantissa: result}));
}
/**
* @dev Subtracts two exponentials, returning a new exponential.
*/
function subExp(Exp memory a, Exp memory b) pure internal returns (Error, Exp memory) {
(Error error, uint result) = sub(a.mantissa, b.mantissa);
return (error, Exp({mantissa: result}));
}
/**
* @dev Multiply an Exp by a scalar, returning a new Exp.
*/
function mulScalar(Exp memory a, uint scalar) pure internal returns (Error, Exp memory) {
(Error err0, uint scaledMantissa) = mul(a.mantissa, scalar);
if (err0 != Error.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
return (Error.NO_ERROR, Exp({mantissa: scaledMantissa}));
}
/**
* @dev Divide an Exp by a scalar, returning a new Exp.
*/
function divScalar(Exp memory a, uint scalar) pure internal returns (Error, Exp memory) {
(Error err0, uint descaledMantissa) = div(a.mantissa, scalar);
if (err0 != Error.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
return (Error.NO_ERROR, Exp({mantissa: descaledMantissa}));
}
/**
* @dev Divide a scalar by an Exp, returning a new Exp.
*/
function divScalarByExp(uint scalar, Exp divisor) pure internal returns (Error, Exp memory) {
/*
We are doing this as:
getExp(mul(expScale, scalar), divisor.mantissa)
How it works:
Exp = a / b;
Scalar = s;
`s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale`
*/
(Error err0, uint numerator) = mul(expScale, scalar);
if (err0 != Error.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
return getExp(numerator, divisor.mantissa);
}
/**
* @dev Multiplies two exponentials, returning a new exponential.
*/
function mulExp(Exp memory a, Exp memory b) pure internal returns (Error, Exp memory) {
(Error err0, uint doubleScaledProduct) = mul(a.mantissa, b.mantissa);
if (err0 != Error.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
// We add half the scale before dividing so that we get rounding instead of truncation.
// See "Listing 6" and text above it at https://accu.org/index.php/journals/1717
// Without this change, a result like 6.6...e-19 will be truncated to 0 instead of being rounded to 1e-18.
(Error err1, uint doubleScaledProductWithHalfScale) = add(halfExpScale, doubleScaledProduct);
if (err1 != Error.NO_ERROR) {
return (err1, Exp({mantissa: 0}));
}
(Error err2, uint product) = div(doubleScaledProductWithHalfScale, expScale);
// The only error `div` can return is Error.DIVISION_BY_ZERO but we control `expScale` and it is not zero.
assert(err2 == Error.NO_ERROR);
return (Error.NO_ERROR, Exp({mantissa: product}));
}
/**
* @dev Divides two exponentials, returning a new exponential.
* (a/scale) / (b/scale) = (a/scale) * (scale/b) = a/b,
* which we can scale as an Exp by calling getExp(a.mantissa, b.mantissa)
*/
function divExp(Exp memory a, Exp memory b) pure internal returns (Error, Exp memory) {
return getExp(a.mantissa, b.mantissa);
}
/**
* @dev Truncates the given exp to a whole number value.
* For example, truncate(Exp{mantissa: 15 * (10**18)}) = 15
*/
function truncate(Exp memory exp) pure internal returns (uint) {
// Note: We are not using careful math here as we're performing a division that cannot fail
return exp.mantissa / 10**18;
}
/**
* @dev Checks if first Exp is less than second Exp.
*/
function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa < right.mantissa; //TODO: Add some simple tests and this in another PR yo.
}
/**
* @dev Checks if left Exp <= right Exp.
*/
function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa <= right.mantissa;
}
/**
* @dev returns true if Exp is exactly zero
*/
function isZeroExp(Exp memory value) pure internal returns (bool) {
return value.mantissa == 0;
}
}
You can’t perform that action at this time.