Skip to content

Commit

Permalink
Add UD21x18 and SD21x18 types (#212)
Browse files Browse the repository at this point in the history
* feat: add UD21x18 type

* feat: add SD21x18 type

* test: remove compiler warnings

* fix: PR problems

* docs: mention new adjacent types in README

---------

Co-authored-by: Paul Razvan Berg <[email protected]>
  • Loading branch information
andreivladbrg and PaulRBerg authored Jun 28, 2024
1 parent 3681681 commit ebe0395
Show file tree
Hide file tree
Showing 39 changed files with 1,403 additions and 73 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,16 @@ the source code, which is well-documented with NatSpec comments.

### Adjacent Value Types

PRBMath provides adjacent value types that serve as abstractions over other vanilla types such as `int64`. The types currently available are:
PRBMath provides adjacent value types that serve as abstractions over other vanilla types:

| Value Type | Underlying Type |
| ---------- | --------------- |
| `SD1x18` | int64 |
| `SD21x18` | int128 |
| `UD2x18` | uint64 |
| `UD21x18` | uint128 |

These are useful if you want to save gas by using a lower bit width integer, e.g. in a struct.
These are useful if you want to save gas by using a lower bit width integer, e.g., in a struct.

Note that these types don't have any mathematical functionality. To do math with them, you will have to unwrap them into a simple integer and then to
the core types `SD59x18` and `UD60x18`.
Expand Down
3 changes: 3 additions & 0 deletions src/Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ uint128 constant MAX_UINT128 = type(uint128).max;
/// @dev The maximum value a uint40 number can have.
uint40 constant MAX_UINT40 = type(uint40).max;

/// @dev The maximum value a uint64 number can have.
uint64 constant MAX_UINT64 = type(uint64).max;

/// @dev The unit number, which the decimal precision of the fixed-point types.
uint256 constant UNIT = 1e18;

Expand Down
25 changes: 25 additions & 0 deletions src/SD21x18.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

/*
██████╗ ██████╗ ██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██╗
██╔══██╗██╔══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝██║ ██║
██████╔╝██████╔╝██████╔╝██╔████╔██║███████║ ██║ ███████║
██╔═══╝ ██╔══██╗██╔══██╗██║╚██╔╝██║██╔══██║ ██║ ██╔══██║
██║ ██║ ██║██████╔╝██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║
╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
███████╗██████╗ ██████╗ ██╗██╗ ██╗ ██╗ █████╗
██╔════╝██╔══██╗╚════██╗███║╚██╗██╔╝███║██╔══██╗
███████╗██║ ██║ █████╔╝╚██║ ╚███╔╝ ╚██║╚█████╔╝
╚════██║██║ ██║██╔═══╝ ██║ ██╔██╗ ██║██╔══██╗
███████║██████╔╝███████╗ ██║██╔╝ ██╗ ██║╚█████╔╝
╚══════╝╚═════╝ ╚══════╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚════╝
*/

import "./sd21x18/Casting.sol";
import "./sd21x18/Constants.sol";
import "./sd21x18/Errors.sol";
import "./sd21x18/ValueType.sol";
25 changes: 25 additions & 0 deletions src/UD21x18.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

/*
██████╗ ██████╗ ██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██╗
██╔══██╗██╔══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝██║ ██║
██████╔╝██████╔╝██████╔╝██╔████╔██║███████║ ██║ ███████║
██╔═══╝ ██╔══██╗██╔══██╗██║╚██╔╝██║██╔══██║ ██║ ██╔══██║
██║ ██║ ██║██████╔╝██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║
╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
██╗ ██╗██████╗ ██████╗ ██╗██╗ ██╗ ██╗ █████╗
██║ ██║██╔══██╗╚════██╗███║╚██╗██╔╝███║██╔══██╗
██║ ██║██║ ██║ █████╔╝╚██║ ╚███╔╝ ╚██║╚█████╔╝
██║ ██║██║ ██║██╔═══╝ ██║ ██╔██╗ ██║██╔══██╗
╚██████╔╝██████╔╝███████╗ ██║██╔╝ ██╗ ██║╚█████╔╝
╚═════╝ ╚═════╝ ╚══════╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚════╝
*/

import "./ud21x18/Casting.sol";
import "./ud21x18/Constants.sol";
import "./ud21x18/Errors.sol";
import "./ud21x18/ValueType.sol";
27 changes: 24 additions & 3 deletions src/casting/Uint128.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ pragma solidity >=0.8.19;

import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_SD21x18 } from "../sd21x18/Constants.sol";
import { SD21x18 } from "../sd21x18/ValueType.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD21x18 } from "../ud21x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";

/// @notice Thrown when trying to cast a uint128 that doesn't fit in SD1x18.
error PRBMath_IntoSD1x18_Overflow(uint128 x);

/// @notice Thrown when trying to cast a uint128 that doesn't fit in SD21x18.
error PRBMath_IntoSD21x18_Overflow(uint128 x);

/// @notice Thrown when trying to cast a uint128 that doesn't fit in UD2x18.
error PRBMath_IntoUD2x18_Overflow(uint128 x);

Expand All @@ -19,14 +25,24 @@ error PRBMath_IntoUD2x18_Overflow(uint128 x);
library PRBMathCastingUint128 {
/// @notice Casts a uint128 number to SD1x18.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_SD1x18`.
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(uint128 x) internal pure returns (SD1x18 result) {
if (x > uint256(int256(uMAX_SD1x18))) {
revert PRBMath_IntoSD1x18_Overflow(x);
}
result = SD1x18.wrap(int64(uint64(x)));
}

/// @notice Casts a uint128 number to SD21x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD21x18`.
function intoSD21x18(uint128 x) internal pure returns (SD21x18 result) {
if (x > uint256(int256(uMAX_SD21x18))) {
revert PRBMath_IntoSD21x18_Overflow(x);
}
result = SD21x18.wrap(int128(x));
}

/// @notice Casts a uint128 number to SD59x18.
/// @dev There is no overflow check because the domain of uint128 is a subset of SD59x18.
function intoSD59x18(uint128 x) internal pure returns (SD59x18 result) {
Expand All @@ -35,17 +51,22 @@ library PRBMathCastingUint128 {

/// @notice Casts a uint128 number to UD2x18.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_SD1x18`.
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(uint128 x) internal pure returns (UD2x18 result) {
if (x > uint64(uMAX_UD2x18)) {
revert PRBMath_IntoUD2x18_Overflow(x);
}
result = UD2x18.wrap(uint64(x));
}

/// @notice Casts a uint128 number to UD21x18.
function intoUD21x18(uint128 x) internal pure returns (UD21x18 result) {
result = UD21x18.wrap(x);
}

/// @notice Casts a uint128 number to UD60x18.
/// @dev There is no overflow check because the domain of uint128 is a subset of UD60x18.
function intoUD60x18(uint128 x) internal pure returns (UD60x18 result) {
result = UD60x18.wrap(uint256(x));
result = UD60x18.wrap(x);
}
}
36 changes: 34 additions & 2 deletions src/casting/Uint256.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,57 @@ pragma solidity >=0.8.19;

import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_SD21x18 } from "../sd21x18/Constants.sol";
import { SD21x18 } from "../sd21x18/ValueType.sol";
import { uMAX_SD59x18 } from "../sd59x18/Constants.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { uMAX_UD21x18 } from "../ud21x18/Constants.sol";
import { UD21x18 } from "../ud21x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";

/// @notice Thrown when trying to cast a uint256 that doesn't fit in SD1x18.
error PRBMath_IntoSD1x18_Overflow(uint256 x);

/// @notice Thrown when trying to cast a uint256 that doesn't fit in SD21x18.
error PRBMath_IntoSD21x18_Overflow(uint256 x);

/// @notice Thrown when trying to cast a uint256 that doesn't fit in SD59x18.
error PRBMath_IntoSD59x18_Overflow(uint256 x);

/// @notice Thrown when trying to cast a uint256 that doesn't fit in UD2x18.
error PRBMath_IntoUD2x18_Overflow(uint256 x);

/// @notice Thrown when trying to cast a uint256 that doesn't fit in UD21x18.
error PRBMath_IntoUD21x18_Overflow(uint256 x);

/// @title PRBMathCastingUint256
/// @notice Casting utilities for uint256.
library PRBMathCastingUint256 {
/// @notice Casts a uint256 number to SD1x18.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_SD1x18`.
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(uint256 x) internal pure returns (SD1x18 result) {
if (x > uint256(int256(uMAX_SD1x18))) {
revert PRBMath_IntoSD1x18_Overflow(x);
}
result = SD1x18.wrap(int64(int256(x)));
}

/// @notice Casts a uint256 number to SD21x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD21x18`.
function intoSD21x18(uint256 x) internal pure returns (SD21x18 result) {
if (x > uint256(int256(uMAX_SD21x18))) {
revert PRBMath_IntoSD21x18_Overflow(x);
}
result = SD21x18.wrap(int128(int256(x)));
}

/// @notice Casts a uint256 number to SD59x18.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_SD59x18`.
/// - x must be less than or equal to `uMAX_SD59x18`.
function intoSD59x18(uint256 x) internal pure returns (SD59x18 result) {
if (x > uint256(uMAX_SD59x18)) {
revert PRBMath_IntoSD59x18_Overflow(x);
Expand All @@ -42,13 +62,25 @@ library PRBMathCastingUint256 {
}

/// @notice Casts a uint256 number to UD2x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(uint256 x) internal pure returns (UD2x18 result) {
if (x > uint256(uMAX_UD2x18)) {
revert PRBMath_IntoUD2x18_Overflow(x);
}
result = UD2x18.wrap(uint64(x));
}

/// @notice Casts a uint256 number to UD2x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_UD21x18`.
function intoUD21x18(uint256 x) internal pure returns (UD21x18 result) {
if (x > uint256(uMAX_UD21x18)) {
revert PRBMath_IntoUD21x18_Overflow(x);
}
result = UD21x18.wrap(uint128(x));
}

/// @notice Casts a uint256 number to UD60x18.
function intoUD60x18(uint256 x) internal pure returns (UD60x18 result) {
result = UD60x18.wrap(x);
Expand Down
18 changes: 16 additions & 2 deletions src/casting/Uint40.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
pragma solidity >=0.8.19;

import { SD1x18 } from "../sd1x18/ValueType.sol";
import { SD21x18 } from "../sd21x18/ValueType.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD21x18 } from "../ud21x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";

/// @title PRBMathCastingUint40
Expand All @@ -15,6 +17,12 @@ library PRBMathCastingUint40 {
result = SD1x18.wrap(int64(uint64(x)));
}

/// @notice Casts a uint40 number into SD21x18.
/// @dev There is no overflow check because the domain of uint40 is a subset of SD21x18.
function intoSD21x18(uint40 x) internal pure returns (SD21x18 result) {
result = SD21x18.wrap(int128(uint128(x)));
}

/// @notice Casts a uint40 number into SD59x18.
/// @dev There is no overflow check because the domain of uint40 is a subset of SD59x18.
function intoSD59x18(uint40 x) internal pure returns (SD59x18 result) {
Expand All @@ -24,12 +32,18 @@ library PRBMathCastingUint40 {
/// @notice Casts a uint40 number into UD2x18.
/// @dev There is no overflow check because the domain of uint40 is a subset of UD2x18.
function intoUD2x18(uint40 x) internal pure returns (UD2x18 result) {
result = UD2x18.wrap(uint64(x));
result = UD2x18.wrap(x);
}

/// @notice Casts a uint40 number into UD21x18.
/// @dev There is no overflow check because the domain of uint40 is a subset of UD21x18.
function intoUD21x18(uint40 x) internal pure returns (UD21x18 result) {
result = UD21x18.wrap((x));
}

/// @notice Casts a uint40 number into UD60x18.
/// @dev There is no overflow check because the domain of uint40 is a subset of UD60x18.
function intoUD60x18(uint40 x) internal pure returns (UD60x18 result) {
result = UD60x18.wrap(uint256(x));
result = UD60x18.wrap(x);
}
}
34 changes: 26 additions & 8 deletions src/sd1x18/Casting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@ pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as CastingErrors;
import { SD21x18 } from "../sd21x18/ValueType.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD21x18 } from "../ud21x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { SD1x18 } from "./ValueType.sol";

/// @notice Casts an SD1x18 number into SD21x18.
/// @dev There is no overflow check because the domain of SD1x18 is a subset of SD21x18.
function intoSD21x18(SD1x18 x) pure returns (SD21x18 result) {
result = SD21x18.wrap(int128(int256(SD1x18.unwrap(x))));
}

/// @notice Casts an SD1x18 number into SD59x18.
/// @dev There is no overflow check because the domain of SD1x18 is a subset of SD59x18.
function intoSD59x18(SD1x18 x) pure returns (SD59x18 result) {
Expand All @@ -24,6 +32,16 @@ function intoUD2x18(SD1x18 x) pure returns (UD2x18 result) {
result = UD2x18.wrap(uint64(xInt));
}

/// @notice Casts an SD1x18 number into UD21x18.
/// - x must be positive.
function intoUD21x18(SD1x18 x) pure returns (UD21x18 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUD21x18_Underflow(x);
}
result = UD21x18.wrap(uint128(uint64(xInt)));
}

/// @notice Casts an SD1x18 number into UD60x18.
/// @dev Requirements:
/// - x must be positive.
Expand All @@ -35,26 +53,26 @@ function intoUD60x18(SD1x18 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(uint64(xInt));
}

/// @notice Casts an SD1x18 number into uint256.
/// @notice Casts an SD1x18 number into uint128.
/// @dev Requirements:
/// - x must be positive.
function intoUint256(SD1x18 x) pure returns (uint256 result) {
function intoUint128(SD1x18 x) pure returns (uint128 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUint256_Underflow(x);
revert CastingErrors.PRBMath_SD1x18_ToUint128_Underflow(x);
}
result = uint256(uint64(xInt));
result = uint128(uint64(xInt));
}

/// @notice Casts an SD1x18 number into uint128.
/// @notice Casts an SD1x18 number into uint256.
/// @dev Requirements:
/// - x must be positive.
function intoUint128(SD1x18 x) pure returns (uint128 result) {
function intoUint256(SD1x18 x) pure returns (uint256 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUint128_Underflow(x);
revert CastingErrors.PRBMath_SD1x18_ToUint256_Underflow(x);
}
result = uint128(uint64(xInt));
result = uint256(uint64(xInt));
}

/// @notice Casts an SD1x18 number into uint40.
Expand Down
2 changes: 1 addition & 1 deletion src/sd1x18/Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SD1x18 constant E = SD1x18.wrap(2_718281828459045235);
int64 constant uMAX_SD1x18 = 9_223372036854775807;
SD1x18 constant MAX_SD1x18 = SD1x18.wrap(uMAX_SD1x18);

/// @dev The maximum value an SD1x18 number can have.
/// @dev The minimum value an SD1x18 number can have.
int64 constant uMIN_SD1x18 = -9_223372036854775808;
SD1x18 constant MIN_SD1x18 = SD1x18.wrap(uMIN_SD1x18);

Expand Down
15 changes: 9 additions & 6 deletions src/sd1x18/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ pragma solidity >=0.8.19;

import { SD1x18 } from "./ValueType.sol";

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD2x18.
/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in UD2x18.
error PRBMath_SD1x18_ToUD2x18_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD60x18.
/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in UD21x18.
error PRBMath_SD1x18_ToUD21x18_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in UD60x18.
error PRBMath_SD1x18_ToUD60x18_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint128.
/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in uint128.
error PRBMath_SD1x18_ToUint128_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint256.
/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in uint256.
error PRBMath_SD1x18_ToUint256_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40.
/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in uint40.
error PRBMath_SD1x18_ToUint40_Overflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40.
/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in uint40.
error PRBMath_SD1x18_ToUint40_Underflow(SD1x18 x);
Loading

0 comments on commit ebe0395

Please sign in to comment.