-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathFixed6.sol
373 lines (337 loc) · 13.3 KB
/
Fixed6.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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/utils/math/SignedMath.sol";
import "../NumberMath.sol";
import "./Fixed18.sol";
import "./UFixed6.sol";
/// @dev Fixed6 type
type Fixed6 is int256;
using Fixed6Lib for Fixed6 global;
type Fixed6Storage is bytes32;
using Fixed6StorageLib for Fixed6Storage global;
/**
* @title Fixed6Lib
* @notice Library for the signed fixed-decimal type.
*/
library Fixed6Lib {
error Fixed6OverflowError(uint256 value);
int256 private constant BASE = 1e6;
Fixed6 public constant ZERO = Fixed6.wrap(0);
Fixed6 public constant ONE = Fixed6.wrap(BASE);
Fixed6 public constant NEG_ONE = Fixed6.wrap(-1 * BASE);
Fixed6 public constant MAX = Fixed6.wrap(type(int256).max);
Fixed6 public constant MIN = Fixed6.wrap(type(int256).min);
/**
* @notice Creates a signed fixed-decimal from an unsigned fixed-decimal
* @param a Unsigned fixed-decimal
* @return New signed fixed-decimal
*/
function from(UFixed6 a) internal pure returns (Fixed6) {
uint256 value = UFixed6.unwrap(a);
if (value > uint256(type(int256).max)) revert Fixed6OverflowError(value);
return Fixed6.wrap(int256(value));
}
/**
* @notice Creates a signed fixed-decimal from a sign and an unsigned fixed-decimal
* @param s Sign
* @param m Unsigned fixed-decimal magnitude
* @return New signed fixed-decimal
*/
function from(int256 s, UFixed6 m) internal pure returns (Fixed6) {
if (s > 0) return from(m);
if (s < 0) {
// Since from(m) multiplies m by BASE, from(m) cannot be type(int256).min
// which is the only value that would overflow when negated. Therefore,
// we can safely negate from(m) without checking for overflow.
unchecked { return Fixed6.wrap(-1 * Fixed6.unwrap(from(m))); }
}
return ZERO;
}
/**
* @notice Creates a signed fixed-decimal from a signed integer
* @param a Signed number
* @return New signed fixed-decimal
*/
function from(int256 a) internal pure returns (Fixed6) {
return Fixed6.wrap(a * BASE);
}
/**
* @notice Creates a signed fixed-decimal from a base-18 signed fixed-decimal
* @param a Base-18 signed fixed-decimal
* @return New signed fixed-decimal
*/
function from(Fixed18 a) internal pure returns (Fixed6) {
return Fixed6.wrap(Fixed18.unwrap(a) / 1e12);
}
/**
* @notice Creates a signed fixed-decimal from a base-18 signed fixed-decimal
* @param a Base-18 signed fixed-decimal
* @param roundOut Whether to round the result away from zero if there is a remainder
* @return New signed fixed-decimal
*/
function from(Fixed18 a, bool roundOut) internal pure returns (Fixed6) {
return roundOut ? Fixed6.wrap(NumberMath.divOut(Fixed18.unwrap(a), 1e12)): from(a);
}
/**
* @notice Returns whether the signed fixed-decimal is equal to zero.
* @param a Signed fixed-decimal
* @return Whether the signed fixed-decimal is zero.
*/
function isZero(Fixed6 a) internal pure returns (bool) {
return Fixed6.unwrap(a) == 0;
}
/**
* @notice Adds two signed fixed-decimals `a` and `b` together
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Resulting summed signed fixed-decimal
*/
function add(Fixed6 a, Fixed6 b) internal pure returns (Fixed6) {
return Fixed6.wrap(Fixed6.unwrap(a) + Fixed6.unwrap(b));
}
/**
* @notice Subtracts signed fixed-decimal `b` from `a`
* @param a Signed fixed-decimal to subtract from
* @param b Signed fixed-decimal to subtract
* @return Resulting subtracted signed fixed-decimal
*/
function sub(Fixed6 a, Fixed6 b) internal pure returns (Fixed6) {
return Fixed6.wrap(Fixed6.unwrap(a) - Fixed6.unwrap(b));
}
/**
* @notice Multiplies two signed fixed-decimals `a` and `b` together
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Resulting multiplied signed fixed-decimal
*/
function mul(Fixed6 a, Fixed6 b) internal pure returns (Fixed6) {
return Fixed6.wrap(Fixed6.unwrap(a) * Fixed6.unwrap(b) / BASE);
}
/**
* @notice Multiplies two signed fixed-decimals `a` and `b` together, rounding the result away from zero if there is a remainder
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Resulting multiplied signed fixed-decimal
*/
function mulOut(Fixed6 a, Fixed6 b) internal pure returns (Fixed6) {
return Fixed6.wrap(NumberMath.divOut(Fixed6.unwrap(a) * Fixed6.unwrap(b), BASE));
}
/**
* @notice Divides signed fixed-decimal `a` by `b`
* @param a Signed fixed-decimal to divide
* @param b Signed fixed-decimal to divide by
* @return Resulting divided signed fixed-decimal
*/
function div(Fixed6 a, Fixed6 b) internal pure returns (Fixed6) {
return Fixed6.wrap(Fixed6.unwrap(a) * BASE / Fixed6.unwrap(b));
}
/**
* @notice Divides signed fixed-decimal `a` by `b`, rounding the result away from zero if there is a remainder
* @param a Signed fixed-decimal to divide
* @param b Signed fixed-decimal to divide by
* @return Resulting divided signed fixed-decimal
*/
function divOut(Fixed6 a, Fixed6 b) internal pure returns (Fixed6) {
return Fixed6Lib.from(sign(a) * sign(b), a.abs().divOut(b.abs()));
}
/**
* @notice Divides unsigned fixed-decimal `a` by `b`
* @dev Does not revert on divide-by-0, instead returns `ONE` for `0/0`, `MAX` for `n/0`, and `MIN` for `-n/0`.
* @param a Unsigned fixed-decimal to divide
* @param b Unsigned fixed-decimal to divide by
* @return Resulting divided unsigned fixed-decimal
*/
function unsafeDiv(Fixed6 a, Fixed6 b) internal pure returns (Fixed6) {
if (isZero(b)) {
if (gt(a, ZERO)) return MAX;
if (lt(a, ZERO)) return MIN;
return ONE;
} else {
return div(a, b);
}
}
/**
* @notice Divides unsigned fixed-decimal `a` by `b`, rounding the result away from zero if there is a remainder
* @dev Does not revert on divide-by-0, instead returns `ONE` for `0/0`, `MAX` for `n/0`, and `MIN` for `-n/0`.
* @param a Unsigned fixed-decimal to divide
* @param b Unsigned fixed-decimal to divide by
* @return Resulting divided unsigned fixed-decimal
*/
function unsafeDivOut(Fixed6 a, Fixed6 b) internal pure returns (Fixed6) {
if (isZero(b)) {
if (gt(a, ZERO)) return MAX;
if (lt(a, ZERO)) return MIN;
return ONE;
} else {
return divOut(a, b);
}
}
/**
* @notice Computes a * b / c without loss of precision due to BASE conversion
* @param a First signed fixed-decimal
* @param b Signed number to multiply by
* @param c Signed number to divide by
* @return Resulting computation
*/
function muldiv(Fixed6 a, int256 b, int256 c) internal pure returns (Fixed6) {
return muldiv(a, Fixed6.wrap(b), Fixed6.wrap(c));
}
/**
* @notice Computes a * b / c without loss of precision due to BASE conversion, rounding the result up to the next integer if there is a remainder
* @param a First signed fixed-decimal
* @param b Signed number to multiply by
* @param c Signed number to divide by
* @return Resulting computation
*/
function muldivOut(Fixed6 a, int256 b, int256 c) internal pure returns (Fixed6) {
return muldivOut(a, Fixed6.wrap(b), Fixed6.wrap(c));
}
/**
* @notice Computes a * b / c without loss of precision due to BASE conversion
* @param a First signed fixed-decimal
* @param b Signed fixed-decimal to multiply by
* @param c Signed fixed-decimal to divide by
* @return Resulting computation
*/
function muldiv(Fixed6 a, Fixed6 b, Fixed6 c) internal pure returns (Fixed6) {
return Fixed6.wrap(Fixed6.unwrap(a) * Fixed6.unwrap(b) / Fixed6.unwrap(c));
}
/**
* @notice Computes a * b / c without loss of precision due to BASE conversion, rounding the result up to the next integer if there is a remainder
* @param a First signed fixed-decimal
* @param b Signed fixed-decimal to multiply by
* @param c Signed fixed-decimal to divide by
* @return Resulting computation
*/
function muldivOut(Fixed6 a, Fixed6 b, Fixed6 c) internal pure returns (Fixed6) {
return Fixed6.wrap(NumberMath.divOut(Fixed6.unwrap(a) * Fixed6.unwrap(b), Fixed6.unwrap(c)));
}
/**
* @notice Returns whether signed fixed-decimal `a` is equal to `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Whether `a` is equal to `b`
*/
function eq(Fixed6 a, Fixed6 b) internal pure returns (bool) {
return compare(a, b) == 1;
}
/**
* @notice Returns whether signed fixed-decimal `a` is greater than `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Whether `a` is greater than `b`
*/
function gt(Fixed6 a, Fixed6 b) internal pure returns (bool) {
return compare(a, b) == 2;
}
/**
* @notice Returns whether signed fixed-decimal `a` is less than `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Whether `a` is less than `b`
*/
function lt(Fixed6 a, Fixed6 b) internal pure returns (bool) {
return compare(a, b) == 0;
}
/**
* @notice Returns whether signed fixed-decimal `a` is greater than or equal to `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Whether `a` is greater than or equal to `b`
*/
function gte(Fixed6 a, Fixed6 b) internal pure returns (bool) {
return gt(a, b) || eq(a, b);
}
/**
* @notice Returns whether signed fixed-decimal `a` is less than or equal to `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Whether `a` is less than or equal to `b`
*/
function lte(Fixed6 a, Fixed6 b) internal pure returns (bool) {
return lt(a, b) || eq(a, b);
}
/**
* @notice Compares the signed fixed-decimals `a` and `b`
* @dev Returns: 2 for greater than
* 1 for equal to
* 0 for less than
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Compare result of `a` and `b`
*/
function compare(Fixed6 a, Fixed6 b) internal pure returns (uint256) {
(int256 au, int256 bu) = (Fixed6.unwrap(a), Fixed6.unwrap(b));
if (au > bu) return 2;
if (au < bu) return 0;
return 1;
}
/**
* @notice Returns a signed fixed-decimal representing the ratio of `a` over `b`
* @param a First signed number
* @param b Second signed number
* @return Ratio of `a` over `b`
*/
function ratio(int256 a, int256 b) internal pure returns (Fixed6) {
return Fixed6.wrap(a * BASE / b);
}
/**
* @notice Returns the minimum of signed fixed-decimals `a` and `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Minimum of `a` and `b`
*/
function min(Fixed6 a, Fixed6 b) internal pure returns (Fixed6) {
return Fixed6.wrap(SignedMath.min(Fixed6.unwrap(a), Fixed6.unwrap(b)));
}
/**
* @notice Returns the maximum of signed fixed-decimals `a` and `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Maximum of `a` and `b`
*/
function max(Fixed6 a, Fixed6 b) internal pure returns (Fixed6) {
return Fixed6.wrap(SignedMath.max(Fixed6.unwrap(a), Fixed6.unwrap(b)));
}
/**
* @notice Converts the signed fixed-decimal into an integer, truncating any decimal portion
* @param a Signed fixed-decimal
* @return Truncated signed number
*/
function truncate(Fixed6 a) internal pure returns (int256) {
return Fixed6.unwrap(a) / BASE;
}
/**
* @notice Returns the sign of the signed fixed-decimal
* @dev Returns: -1 for negative
* 0 for zero
* 1 for positive
* @param a Signed fixed-decimal
* @return Sign of the signed fixed-decimal
*/
function sign(Fixed6 a) internal pure returns (int256) {
if (Fixed6.unwrap(a) > 0) return 1;
if (Fixed6.unwrap(a) < 0) return -1;
return 0;
}
/**
* @notice Returns the absolute value of the signed fixed-decimal
* @param a Signed fixed-decimal
* @return Absolute value of the signed fixed-decimal
*/
function abs(Fixed6 a) internal pure returns (UFixed6) {
return UFixed6.wrap(SignedMath.abs(Fixed6.unwrap(a)));
}
}
library Fixed6StorageLib {
function read(Fixed6Storage self) internal view returns (Fixed6 value) {
assembly ("memory-safe") {
value := sload(self)
}
}
function store(Fixed6Storage self, Fixed6 value) internal {
assembly ("memory-safe") {
sstore(self, value)
}
}
}