-
Notifications
You must be signed in to change notification settings - Fork 50
/
Copy pathExponential.sol
174 lines (145 loc) · 6.15 KB
/
Exponential.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
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;
}
}