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
240 changes: 240 additions & 0 deletions src/lib/LibDecimalFloat.sol

Large diffs are not rendered by default.

27 changes: 26 additions & 1 deletion test/src/lib/LibDecimalFloat.abs.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,34 @@ pragma solidity =0.8.25;

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

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

contract LibDecimalFloatAbsTest is Test {
using LibDecimalFloat for Float;

function absExternal(int256 signedCoefficient, int256 exponent) external pure returns (int256, int256) {
return LibDecimalFloat.abs(signedCoefficient, exponent);
}

function absExternal(Float memory float) external pure returns (Float memory) {
return LibDecimalFloat.abs(float);
}
/// Validate that operations using stack-based parameters (int256, int256)
/// and memory-based parameters (Float struct) yield identical results.

function testAbsMem(Float memory float) external {
try this.absExternal(float.signedCoefficient, float.exponent) returns (
int256 signedCoefficient, int256 exponent
) {
Float memory floatAbs = this.absExternal(float);
assertEq(signedCoefficient, floatAbs.signedCoefficient);
assertEq(exponent, floatAbs.exponent);
} catch (bytes memory err) {
vm.expectRevert(err);
this.absExternal(float);
}
}

/// Anything non negative is identity.
function testAbsNonNegative(int256 signedCoefficient, int256 exponent) external pure {
signedCoefficient = bound(signedCoefficient, 0, type(int256).max);
Expand Down
30 changes: 29 additions & 1 deletion test/src/lib/LibDecimalFloat.add.t.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,40 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

import {LibDecimalFloat, EXPONENT_MIN, EXPONENT_MAX, ADD_MAX_EXPONENT_DIFF} from "src/lib/LibDecimalFloat.sol";
import {LibDecimalFloat, EXPONENT_MIN, EXPONENT_MAX, ADD_MAX_EXPONENT_DIFF, Float} from "src/lib/LibDecimalFloat.sol";
import {LibDecimalFloatImplementation} from "src/lib/implementation/LibDecimalFloatImplementation.sol";

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

contract LibDecimalFloatDecimalAddTest is Test {
using LibDecimalFloat for Float;

function addExternal(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
external
pure
returns (int256, int256)
{
return LibDecimalFloat.add(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}

function addExternal(Float memory a, Float memory b) external pure returns (int256, int256) {
return LibDecimalFloat.add(a.signedCoefficient, a.exponent, b.signedCoefficient, b.exponent);
}

/// Stack and mem are the same.
function testAddMem(Float memory a, Float memory b) external {
try this.addExternal(a.signedCoefficient, a.exponent, b.signedCoefficient, b.exponent) returns (
int256 signedCoefficient, int256 exponent
) {
Float memory resultMem = a.add(b);
assertEq(signedCoefficient, resultMem.signedCoefficient);
assertEq(exponent, resultMem.exponent);
} catch (bytes memory err) {
vm.expectRevert(err);
a.add(b);
}
}

/// Simple 0 add 0
/// 0 + 0 = 0
function testAddZero() external pure {
Expand Down
35 changes: 34 additions & 1 deletion test/src/lib/LibDecimalFloat.decimal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import {
ExponentOverflow,
NORMALIZED_MAX,
NORMALIZED_MIN,
NegativeFixedDecimalConversion
NegativeFixedDecimalConversion,
Float
} from "src/lib/LibDecimalFloat.sol";
import {LibDecimalFloatImplementation} from "src/lib/implementation/LibDecimalFloatImplementation.sol";

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

contract LibDecimalFloatDecimalTest is Test {
using LibDecimalFloat for Float;

function toFixedDecimalLossyExternal(int256 signedCoefficient, int256 exponent, uint8 decimals)
external
pure
Expand All @@ -21,6 +24,36 @@ contract LibDecimalFloatDecimalTest is Test {
return LibDecimalFloat.toFixedDecimalLossy(signedCoefficient, exponent, decimals);
}

function toFixedDecimalLossyExternal(Float memory float, uint8 decimals) external pure returns (uint256, bool) {
return float.toFixedDecimalLossy(decimals);
}

/// Memory version of from behaves same as stack version.
function testFromFixedDecimalLossyMem(uint256 value, uint8 decimals) external pure {
(int256 signedCoefficient, int256 exponent, bool lossless) =
LibDecimalFloat.fromFixedDecimalLossy(value, decimals);

(Float memory float, bool floatLossless) = LibDecimalFloat.fromFixedDecimalLossyMem(value, decimals);
assertEq(float.signedCoefficient, signedCoefficient, "signedCoefficient");
assertEq(float.exponent, exponent, "exponent");
assertEq(floatLossless, lossless, "lossless");
}

/// Memory version of to behaves same as stack version.
function testToFixedDecimalLossyMem(Float memory float, uint8 decimals) external {
try this.toFixedDecimalLossyExternal(float.signedCoefficient, float.exponent, decimals) returns (
uint256 value, bool lossless
) {
(uint256 valueOut, bool losslessOut) = float.toFixedDecimalLossy(decimals);
assertEq(value, valueOut, "value");
assertEq(lossless, losslessOut, "lossless");
} catch (bytes memory err) {
vm.expectRevert(err);
(uint256 valueOut, bool losslessOut) = this.toFixedDecimalLossyExternal(float, decimals);
(valueOut, losslessOut);
}
}

/// Round trip from/to decimal values without precision loss
function testFixedDecimalRoundTripLossless(uint256 value, uint8 decimals) external pure {
value = bound(value, 0, uint256(NORMALIZED_MAX));
Expand Down
49 changes: 48 additions & 1 deletion test/src/lib/LibDecimalFloat.decimalLossless.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,62 @@ import {
SIGNED_NORMALIZED_MAX,
NORMALIZED_MAX,
LossyConversionFromFloat,
LossyConversionToFloat
LossyConversionToFloat,
Float
} from "../../../src/lib/LibDecimalFloat.sol";
import {Test} from "forge-std/Test.sol";

contract LibDecimalFloatDecimalLosslessTest is Test {
using LibDecimalFloat for Float;

function fromFixedDecimalLosslessExternal(uint256 value, uint8 decimals) external pure returns (int256, int256) {
return LibDecimalFloat.fromFixedDecimalLossless(value, decimals);
}

function fromFixedDecimalLosslessMemExternal(uint256 value, uint8 decimals) external pure returns (Float memory) {
return LibDecimalFloat.fromFixedDecimalLosslessMem(value, decimals);
}

function toFixedDecimalLosslessExternal(int256 signedCoefficient, int256 exponent, uint8 decimals)
external
pure
returns (uint256)
{
return LibDecimalFloat.toFixedDecimalLossless(signedCoefficient, exponent, decimals);
}

function toFixedDecimalLosslessMemExternal(Float memory float, uint8 decimals) external pure returns (uint256) {
return float.toFixedDecimalLossless(decimals);
}

/// Memory version of from behaves the same as stack version.
function testFromFixedDecimalLosslessMem(uint256 value, uint8 decimals) external {
(,, bool losslessPreflight) = LibDecimalFloat.fromFixedDecimalLossy(value, decimals);
if (!losslessPreflight) {
vm.expectRevert(
abi.encodeWithSelector(LossyConversionToFloat.selector, value / 10, 1 - int256(uint256(decimals)))
);
}
(Float memory float) = LibDecimalFloat.fromFixedDecimalLosslessMem(value, decimals);
(int256 signedCoefficient, int256 exponent) = LibDecimalFloat.fromFixedDecimalLossless(value, decimals);
assertEq(float.signedCoefficient, signedCoefficient);
assertEq(float.exponent, exponent);
}

/// Memory version of to behaves the same as stack version.
function testToFixedDecimalLosslessMem(Float memory float, uint8 decimals) external {
try this.toFixedDecimalLosslessExternal(float.signedCoefficient, float.exponent, decimals) returns (
uint256 value
) {
uint256 valueFloat = float.toFixedDecimalLossless(decimals);
assertEq(valueFloat, value);
} catch (bytes memory err) {
vm.expectRevert(err);
uint256 valueFloat = this.toFixedDecimalLosslessMemExternal(float, decimals);
(valueFloat);
}
}

function testToFixedDecimalLosslessPass(int256 signedCoefficient, int256 exponent, uint8 decimals) external pure {
signedCoefficient = bound(signedCoefficient, 0, 1e18);
exponent = bound(exponent, 0, 30);
Expand Down
30 changes: 29 additions & 1 deletion test/src/lib/LibDecimalFloat.divide.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,39 @@
pragma solidity =0.8.25;

import {THREES, ONES} from "../../lib/LibCommonResults.sol";
import {LibDecimalFloat, EXPONENT_MIN, EXPONENT_MAX} from "src/lib/LibDecimalFloat.sol";
import {LibDecimalFloat, EXPONENT_MIN, EXPONENT_MAX, Float} from "src/lib/LibDecimalFloat.sol";

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

contract LibDecimalFloatDivideTest is Test {
using LibDecimalFloat for Float;

function divideExternal(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
external
pure
returns (int256, int256)
{
return LibDecimalFloat.divide(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}

function divideExternal(Float memory floatA, Float memory floatB) external pure returns (Float memory) {
return LibDecimalFloat.divide(floatA, floatB);
}
/// Stack and mem are the same.

function testDivideMem(Float memory a, Float memory b) external {
try this.divideExternal(a.signedCoefficient, a.exponent, b.signedCoefficient, b.exponent) returns (
int256 signedCoefficient, int256 exponent
) {
Float memory float = this.divideExternal(a, b);
assertEq(signedCoefficient, float.signedCoefficient);
assertEq(exponent, float.exponent);
} catch (bytes memory err) {
vm.expectRevert(err);
this.divideExternal(a, b);
}
}

function checkDivision(
int256 signedCoefficientA,
int256 exponentA,
Expand Down
27 changes: 26 additions & 1 deletion test/src/lib/LibDecimalFloat.eq.t.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

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

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

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

contract LibDecimalFloatEqTest is Test {
using LibDecimalFloat for Float;

function eqExternal(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
external
pure
returns (bool)
{
return LibDecimalFloat.eq(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}

function eqExternal(Float memory floatA, Float memory floatB) external pure returns (bool) {
return LibDecimalFloat.eq(floatA, floatB);
}
/// Test to verify that stack-based and memory-based implementations produce the same results.

function testEqMem(Float memory a, Float memory b) external {
try this.eqExternal(a.signedCoefficient, a.exponent, b.signedCoefficient, b.exponent) returns (bool eq) {
bool actual = this.eqExternal(a, b);
assertEq(eq, actual);
} catch (bytes memory err) {
vm.expectRevert(err);
this.eqExternal(a, b);
}
}

function testEqReference(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
external
pure
Expand Down
26 changes: 25 additions & 1 deletion test/src/lib/LibDecimalFloat.floor.t.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

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

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

contract LibDecimalFloatFloorTest is Test {
using LibDecimalFloat for Float;

function floorExternal(int256 signedCoefficient, int256 exponent) external pure returns (int256, int256) {
return LibDecimalFloat.floor(signedCoefficient, exponent);
}

function floorExternal(Float memory float) external pure returns (Float memory) {
return LibDecimalFloat.floor(float);
}
/// Stack and mem are the same.
Comment thread
thedavidmeister marked this conversation as resolved.

function testFloorMem(Float memory float) external {
try this.floorExternal(float.signedCoefficient, float.exponent) returns (
int256 signedCoefficient, int256 exponent
) {
Float memory floatFloor = this.floorExternal(float);
assertEq(signedCoefficient, floatFloor.signedCoefficient);
assertEq(exponent, floatFloor.exponent);
} catch (bytes memory err) {
vm.expectRevert(err);
this.floorExternal(float);
}
}

function testFloorNotReverts(int256 x, int256 exponentX) external pure {
LibDecimalFloat.floor(x, exponentX);
}
Expand Down
26 changes: 25 additions & 1 deletion test/src/lib/LibDecimalFloat.frac.t.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

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

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

contract LibDecimalFloatFracTest is Test {
using LibDecimalFloat for Float;

function fracExternal(int256 signedCoefficient, int256 exponent) external pure returns (int256, int256) {
return LibDecimalFloat.frac(signedCoefficient, exponent);
}

function fracExternal(Float memory float) external pure returns (Float memory) {
return LibDecimalFloat.frac(float);
}
/// Test to verify that stack-based and memory-based implementations produce the same results.

function testFracMem(Float memory float) external {
try this.fracExternal(float.signedCoefficient, float.exponent) returns (
int256 signedCoefficient, int256 exponent
) {
Float memory floatFrac = this.fracExternal(float);
assertEq(signedCoefficient, floatFrac.signedCoefficient);
assertEq(exponent, floatFrac.exponent);
} catch (bytes memory err) {
vm.expectRevert(err);
this.fracExternal(float);
}
}

function testFracNotReverts(int256 x, int256 exponentX) external pure {
LibDecimalFloat.frac(x, exponentX);
}
Expand Down
27 changes: 26 additions & 1 deletion test/src/lib/LibDecimalFloat.gt.t.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

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

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

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

contract LibDecimalFloatGtTest is Test {
using LibDecimalFloat for Float;

function gtExternal(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
external
pure
returns (bool)
{
return LibDecimalFloat.gt(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}

function gtExternal(Float memory floatA, Float memory floatB) external pure returns (bool) {
return LibDecimalFloat.gt(floatA, floatB);
}
/// Stack and mem are the same.

function testGtMem(Float memory a, Float memory b) external {
try this.gtExternal(a.signedCoefficient, a.exponent, b.signedCoefficient, b.exponent) returns (bool gt) {
bool actual = this.gtExternal(a, b);
assertEq(gt, actual);
} catch (bytes memory err) {
vm.expectRevert(err);
this.gtExternal(a, b);
}
}

function testGtReference(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
external
pure
Expand Down
Loading
Loading