Created
October 25, 2019 11:58
-
-
Save beenhero/734172f7e0811e235f724b0c424fb7cb to your computer and use it in GitHub Desktop.
Exponential.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// File: contracts/Exponential.sol | |
pragma solidity ^0.5.8; | |
/** | |
* @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 CarefulMath { | |
uint constant expScale = 1e18; | |
uint constant halfExpScale = expScale/2; | |
uint constant mantissaOne = expScale; | |
struct Exp { | |
uint mantissa; | |
} | |
/** | |
* @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 (MathError, Exp memory) { | |
(MathError err0, uint scaledNumerator) = mulUInt(num, expScale); | |
if (err0 != MathError.NO_ERROR) { | |
return (err0, Exp({mantissa: 0})); | |
} | |
(MathError err1, uint rational) = divUInt(scaledNumerator, denom); | |
if (err1 != MathError.NO_ERROR) { | |
return (err1, Exp({mantissa: 0})); | |
} | |
return (MathError.NO_ERROR, Exp({mantissa: rational})); | |
} | |
/** | |
* @dev Adds two exponentials, returning a new exponential. | |
*/ | |
function addExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { | |
(MathError error, uint result) = addUInt(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 (MathError, Exp memory) { | |
(MathError error, uint result) = subUInt(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 (MathError, Exp memory) { | |
(MathError err0, uint scaledMantissa) = mulUInt(a.mantissa, scalar); | |
if (err0 != MathError.NO_ERROR) { | |
return (err0, Exp({mantissa: 0})); | |
} | |
return (MathError.NO_ERROR, Exp({mantissa: scaledMantissa})); | |
} | |
/** | |
* @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer. | |
*/ | |
function mulScalarTruncate(Exp memory a, uint scalar) pure internal returns (MathError, uint) { | |
(MathError err, Exp memory product) = mulScalar(a, scalar); | |
if (err != MathError.NO_ERROR) { | |
return (err, 0); | |
} | |
return (MathError.NO_ERROR, truncate(product)); | |
} | |
/** | |
* @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer. | |
*/ | |
function mulScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (MathError, uint) { | |
(MathError err, Exp memory product) = mulScalar(a, scalar); | |
if (err != MathError.NO_ERROR) { | |
return (err, 0); | |
} | |
return addUInt(truncate(product), addend); | |
} | |
/** | |
* @dev Divide an Exp by a scalar, returning a new Exp. | |
*/ | |
function divScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) { | |
(MathError err0, uint descaledMantissa) = divUInt(a.mantissa, scalar); | |
if (err0 != MathError.NO_ERROR) { | |
return (err0, Exp({mantissa: 0})); | |
} | |
return (MathError.NO_ERROR, Exp({mantissa: descaledMantissa})); | |
} | |
/** | |
* @dev Divide a scalar by an Exp, returning a new Exp. | |
*/ | |
function divScalarByExp(uint scalar, Exp memory divisor) pure internal returns (MathError, Exp memory) { | |
/* | |
We are doing this as: | |
getExp(mulUInt(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` | |
*/ | |
(MathError err0, uint numerator) = mulUInt(expScale, scalar); | |
if (err0 != MathError.NO_ERROR) { | |
return (err0, Exp({mantissa: 0})); | |
} | |
return getExp(numerator, divisor.mantissa); | |
} | |
/** | |
* @dev Divide a scalar by an Exp, then truncate to return an unsigned integer. | |
*/ | |
function divScalarByExpTruncate(uint scalar, Exp memory divisor) pure internal returns (MathError, uint) { | |
(MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor); | |
if (err != MathError.NO_ERROR) { | |
return (err, 0); | |
} | |
return (MathError.NO_ERROR, truncate(fraction)); | |
} | |
/** | |
* @dev Multiplies two exponentials, returning a new exponential. | |
*/ | |
function mulExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { | |
(MathError err0, uint doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa); | |
if (err0 != MathError.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. | |
(MathError err1, uint doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct); | |
if (err1 != MathError.NO_ERROR) { | |
return (err1, Exp({mantissa: 0})); | |
} | |
(MathError err2, uint product) = divUInt(doubleScaledProductWithHalfScale, expScale); | |
// The only error `div` can return is MathError.DIVISION_BY_ZERO but we control `expScale` and it is not zero. | |
assert(err2 == MathError.NO_ERROR); | |
return (MathError.NO_ERROR, Exp({mantissa: product})); | |
} | |
/** | |
* @dev Multiplies two exponentials given their mantissas, returning a new exponential. | |
*/ | |
function mulExp(uint a, uint b) pure internal returns (MathError, Exp memory) { | |
return mulExp(Exp({mantissa: a}), Exp({mantissa: b})); | |
} | |
/** | |
* @dev Multiplies three exponentials, returning a new exponential. | |
*/ | |
function mulExp3(Exp memory a, Exp memory b, Exp memory c) pure internal returns (MathError, Exp memory) { | |
(MathError err, Exp memory ab) = mulExp(a, b); | |
if (err != MathError.NO_ERROR) { | |
return (err, ab); | |
} | |
return mulExp(ab, c); | |
} | |
/** | |
* @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 (MathError, Exp memory) { | |
return getExp(a.mantissa, b.mantissa); | |
} | |
/** | |
* @dev Truncates the given exp to a whole number value. | |
* For example, truncate(Exp{mantissa: 15 * expScale}) = 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 / expScale; | |
} | |
/** | |
* @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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment