Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
284 changes: 157 additions & 127 deletions .gas-snapshot

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions src/concrete/DecimalFloat.sol
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,24 @@ contract DecimalFloat {
return a.gt(b);
}

/// Exposes `LibDecimalFloat.lte` for offchain use.
/// @param a The first float to compare.
/// @param b The second float to compare.
/// @return True if the first float is less than or equal to the second,
/// false otherwise.
function lte(Float a, Float b) external pure returns (bool) {
return a.lte(b);
}

/// Exposes `LibDecimalFloat.gte` for offchain use.
/// @param a The first float to compare.
/// @param b The second float to compare.
/// @return True if the first float is greater than or equal to the second,
/// false otherwise.
function gte(Float a, Float b) external pure returns (bool) {
return a.gte(b);
}

/// Exposes `LibDecimalFloat.frac` for offchain use.
/// @param a The float to get the fractional part of.
/// @return The fractional part of the float.
Expand Down
24 changes: 24 additions & 0 deletions src/lib/LibDecimalFloat.sol
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,30 @@ library LibDecimalFloat {
return signedCoefficientA > signedCoefficientB;
}

/// Numeric less than or equal to for floats.
/// A float is less than or equal to another if its numeric value is less
/// than or equal to the other. For example, 1e2 is less than or equal to 1e3
/// and 1e2 is less than or equal to 1e2.
function lte(Float a, Float b) internal pure returns (bool) {
(int256 signedCoefficientA, int256 exponentA) = a.unpack();
(int256 signedCoefficientB, int256 exponentB) = b.unpack();
(signedCoefficientA, signedCoefficientB) =
LibDecimalFloatImplementation.compareRescale(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
return signedCoefficientA <= signedCoefficientB;
}

/// Numeric greater than or equal to for floats.
/// A float is greater than or equal to another if its numeric value is
/// greater than or equal to the other. For example, 1e3 is greater than or
/// equal to 1e2 and 1e2 is greater than or equal to 1e2.
function gte(Float a, Float b) internal pure returns (bool) {
(int256 signedCoefficientA, int256 exponentA) = a.unpack();
(int256 signedCoefficientB, int256 exponentB) = b.unpack();
(signedCoefficientA, signedCoefficientB) =
LibDecimalFloatImplementation.compareRescale(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
return signedCoefficientA >= signedCoefficientB;
}

/// Fractional component of a float.
/// @param float The float to frac.
function frac(Float float) internal pure returns (Float) {
Expand Down
16 changes: 16 additions & 0 deletions test/lib/LibDecimalFloatSlow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,22 @@ library LibDecimalFloatSlow {
return eqSlow(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}

function gteSlow(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
internal
pure
returns (bool)
{
return !ltSlow(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}

function lteSlow(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
internal
pure
returns (bool)
{
return !gtSlow(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}

function gtSlow(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
internal
pure
Expand Down
27 changes: 27 additions & 0 deletions test/src/concrete/DecimalFloat.gte.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol";
import {Test} from "forge-std/Test.sol";
import {DecimalFloat} from "src/concrete/DecimalFloat.sol";

contract DecimalFloatGteTest is Test {
using LibDecimalFloat for Float;

function gteExternal(Float a, Float b) external pure returns (bool) {
return a.gte(b);
}

function testGteDeployed(Float a, Float b) external {
DecimalFloat deployed = new DecimalFloat();

try this.gteExternal(a, b) returns (bool c) {
bool deployedC = deployed.gte(a, b);

assertEq(c, deployedC);
} catch (bytes memory err) {
vm.expectRevert(err);
deployed.gte(a, b);
}
}
}
27 changes: 27 additions & 0 deletions test/src/concrete/DecimalFloat.lte.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol";
import {Test} from "forge-std/Test.sol";
import {DecimalFloat} from "src/concrete/DecimalFloat.sol";

contract DecimalFloatLteTest is Test {
using LibDecimalFloat for Float;

function lteExternal(Float a, Float b) external pure returns (bool) {
return a.lte(b);
}

function testLteDeployed(Float a, Float b) external {
DecimalFloat deployed = new DecimalFloat();

try this.lteExternal(a, b) returns (bool c) {
bool deployedC = deployed.lte(a, b);

assertEq(c, deployedC);
} catch (bytes memory err) {
vm.expectRevert(err);
deployed.lte(a, b);
}
}
}
4 changes: 2 additions & 2 deletions test/src/lib/LibDecimalFloat.gt.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ contract LibDecimalFloatGtTest is Test {
}

/// x !> x
function testGtX(int224 x) external pure {
Float a = LibDecimalFloat.packLossless(x, 0);
function testGtX(int224 x, int32 exponent) external pure {
Float a = LibDecimalFloat.packLossless(x, exponent);
bool gt = a.gt(a);
assertTrue(!gt);
}
Expand Down
136 changes: 136 additions & 0 deletions test/src/lib/LibDecimalFloat.gte.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol";

import {LibDecimalFloatSlow} from "test/lib/LibDecimalFloatSlow.sol";

import {Test} from "forge-std/Test.sol";

contract LibDecimalFloatGteTest is Test {
using LibDecimalFloat for Float;

function testGteReference(int224 signedCoefficientA, int32 exponentA, int224 signedCoefficientB, int32 exponentB)
external
pure
{
Float a = LibDecimalFloat.packLossless(signedCoefficientA, exponentA);
Float b = LibDecimalFloat.packLossless(signedCoefficientB, exponentB);
bool actual = a.gte(b);
bool expected = LibDecimalFloatSlow.gteSlow(signedCoefficientA, exponentA, signedCoefficientB, exponentB);

assertEq(actual, expected);
}

/// x >= x
function testGteX(int224 x, int32 exponent) external pure {
Float a = LibDecimalFloat.packLossless(x, exponent);
bool gte = a.gte(a);
assertTrue(gte);
}

/// xeX >= xeX
function testGteOneEAny(Float a) external pure {
bool gte = a.gte(a);
assertTrue(gte);
}

/// xeX >= xeY if X >= Y && x > 0
function testGteXEAnyVsXEAny(int256 x, int32 exponentA, int32 exponentB) external pure {
x = bound(x, 1, type(int224).max);
Float a = LibDecimalFloat.packLossless(x, exponentA);
Float b = LibDecimalFloat.packLossless(x, exponentB);
bool gte = a.gte(b);

assertEq(gte, exponentA >= exponentB);

// Reverse the order.
gte = b.gte(a);
assertEq(gte, exponentB >= exponentA);
}

/// xeX >= xeY if X <= Y && x < 0
function testGteXEAnyVsXEAnyNegative(int256 x, int32 exponentA, int32 exponentB) external pure {
x = bound(x, type(int224).min, -1);
Float a = LibDecimalFloat.packLossless(x, exponentA);
Float b = LibDecimalFloat.packLossless(x, exponentB);
bool gte = a.gte(b);

assertEq(gte, exponentA <= exponentB);

// Reverse the order.
gte = b.gte(a);
assertEq(gte, exponentB <= exponentA);
}

/// xeX >= xeY if x == 0
function testGteZero(int32 exponentA, int32 exponentB) external pure {
Float a = LibDecimalFloat.packLossless(0, exponentA);
Float b = LibDecimalFloat.packLossless(0, exponentB);
bool gte = a.gte(b);
assertTrue(gte);
// Reverse the order.
gte = b.gte(a);
assertTrue(gte);
}

/// xeX >= yeY if x >= 0 && y < 0
function testGteXPositiveYNegative(int256 x, int32 exponentX, int256 y, int32 exponentY) external pure {
x = bound(x, 0, type(int224).max);
y = bound(y, type(int224).min, -1);
Float a = LibDecimalFloat.packLossless(x, exponentX);
Float b = LibDecimalFloat.packLossless(y, exponentY);
bool gte = a.gte(b);
assertTrue(gte);

// Reverse the order.
gte = b.gte(a);
assertTrue(!gte);
}

/// xeX >= yeY if xeX !< yeY
function testGteXNotLtY(Float a, Float b) external pure {
bool gte = a.gte(b);
bool lt = a.lt(b);

assertEq(gte, !lt);
}

/// xeX >= yeY if xeX >= 0 && yeY == 0
function testGteXPositiveYZero(int256 x, int32 exponentX, int32 exponentZero) external pure {
x = bound(x, 0, type(int224).max);
Float a = LibDecimalFloat.packLossless(x, exponentX);
Float b = LibDecimalFloat.packLossless(0, exponentZero);
bool gte = a.gte(b);
assertTrue(gte);
}

function testGteGasDifferentSigns() external pure {
Float a = LibDecimalFloat.packLossless(1, 0);
Float b = LibDecimalFloat.packLossless(-1, 0);
a.gte(b);
}

function testGteGasAZero() external pure {
Float a = LibDecimalFloat.packLossless(0, 0);
Float b = LibDecimalFloat.packLossless(1, 0);
a.gte(b);
}

function testGteGasBZero() external pure {
Float a = LibDecimalFloat.packLossless(1, 0);
Float b = LibDecimalFloat.packLossless(0, 0);
a.gte(b);
}

function testGteGasBothZero() external pure {
Float a = LibDecimalFloat.packLossless(0, 0);
a.gte(a);
}

function testGteGasExponentDiffOverflow() external pure {
Float a = LibDecimalFloat.packLossless(1, type(int32).max);
Float b = LibDecimalFloat.packLossless(1, type(int32).min);
a.gte(b);
}
}
Loading
Loading