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
17 changes: 10 additions & 7 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,14 @@ LibDecimalFloatPowerTest:testRoundTrip() (gas: 1343164)
LibDecimalFloatSubTest:testSubIsAdd(int256,int256,int256,int256) (runs: 5099, μ: 15312, ~: 15201)
LibDecimalFloatSubTest:testSubMem((int256,int256),(int256,int256)) (runs: 5099, μ: 8998, ~: 9242)
LibDecimalFloatSubTest:testSubMinSignedValue(int256,int256,int256) (runs: 5099, μ: 15556, ~: 15464)
LibFormatDecimalFloatTest:testFormatMem((int256,int256)) (runs: 5099, μ: 6030, ~: 6688)
LibFormatDecimalFloatTest:testRoundTrip(uint256) (runs: 5099, μ: 23970, ~: 18699)
LibLogTableBytesTest:testToBytesAntiLogTableDec() (gas: 153225)
LibLogTableBytesTest:testToBytesAntiLogTableDecSmall() (gas: 158036)
LibLogTableBytesTest:testToBytesLogTableDec() (gas: 137284)
LibLogTableBytesTest:testToBytesLogTableDecSmall() (gas: 141767)
LibLogTableBytesTest:testToBytesLogTableDecSmallAlt() (gas: 18049)
LibParseDecimalFloatTest:testParseDecimalFloatEmpty() (gas: 4075)
LibParseDecimalFloatTest:testParseDecimalFloatEmpty() (gas: 4098)
LibParseDecimalFloatTest:testParseDecimalFloatExponentRevert() (gas: 4115)
LibParseDecimalFloatTest:testParseDecimalFloatExponentRevert2() (gas: 5291)
LibParseDecimalFloatTest:testParseDecimalFloatExponentRevert3() (gas: 5375)
Expand All @@ -193,17 +195,18 @@ LibParseDecimalFloatTest:testParseLiteralDecimalFloatDecimals() (gas: 378356)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatDotE() (gas: 4157)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatDotE0() (gas: 4135)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatDotRevert() (gas: 4113)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatDotRevert2() (gas: 4135)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatDotRevert3() (gas: 5076)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatDotRevert2() (gas: 4113)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatDotRevert3() (gas: 5120)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatEDot() (gas: 4159)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatExponentRevert5() (gas: 4145)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatExponentRevert6() (gas: 4092)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatExponents() (gas: 393083)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatFuzz(uint256,uint8,bool) (runs: 5099, μ: 45464, ~: 36819)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatFuzz(uint256,uint8,bool) (runs: 5099, μ: 45507, ~: 36636)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatLeadingZeros() (gas: 59042)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatNegativeE() (gas: 6025)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatNegativeE() (gas: 6048)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatNegativeFrac() (gas: 5095)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatPrecisionRevert0() (gas: 27442)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatPrecisionRevert1() (gas: 27330)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatPrecisionRevert1() (gas: 27353)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatSpecific() (gas: 22682)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatUnrelated() (gas: 31978)
LibParseDecimalFloatTest:testParseLiteralDecimalFloatUnrelated() (gas: 31978)
LibParseDecimalFloatTest:testParseMem(string) (runs: 5099, μ: 9205, ~: 9168)
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "lib/rain.string"]
path = lib/rain.string
url = https://github.com/rainlanguage/rain.string
[submodule "lib/rain.math.fixedpoint"]
path = lib/rain.math.fixedpoint
url = https://github.com/rainlanguage/rain.math.fixedpoint
1 change: 1 addition & 0 deletions lib/rain.math.fixedpoint
Submodule rain.math.fixedpoint added at 6f8f7f
2 changes: 1 addition & 1 deletion slither.config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"detectors_to_exclude": "unused-imports,solc-version,dead-code,different-pragma-directives-are-used,assembly-usage,similar-names,naming-convention",
"filter_paths": "test/,lib/forge-std"
"filter_paths": "test/,lib/forge-std,lib/rain.math.fixedpoint,openzeppelin-contracts"
}
26 changes: 26 additions & 0 deletions src/lib/format/LibFormatDecimalFloat.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 thedavidmeister
pragma solidity ^0.8.25;

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

import {LibFixedPointDecimalFormat} from "rain.math.fixedpoint/lib/format/LibFixedPointDecimalFormat.sol";

library LibFormatDecimalFloat {
/// Format a decimal float as a string.
/// Currently is a thin wrapper around converting to a fixed point decimal
/// and then formatting that as a string.
/// In the future this may be extended to support a wider range of possible
/// values.
/// @param signedCoefficient The signed coefficient of the decimal float.
/// @param exponent The exponent of the decimal float.
/// @return The string representation of the decimal float.
function toDecimalString(int256 signedCoefficient, int256 exponent) internal pure returns (string memory) {
uint256 decimal18Value = LibDecimalFloat.toFixedDecimalLossless(signedCoefficient, exponent, 18);
return LibFixedPointDecimalFormat.fixedPointToDecimalString(decimal18Value);
}

function toDecimalString(Float memory float) internal pure returns (string memory) {
return toDecimalString(float.signedCoefficient, float.exponent);
}
}
14 changes: 13 additions & 1 deletion src/lib/parse/LibParseDecimalFloat.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import {LibParseDecimal} from "rain.string/lib/parse/LibParseDecimal.sol";
import {MalformedExponentDigits, ParseDecimalPrecisionLoss, MalformedDecimalPoint} from "../../error/ErrParse.sol";
import {ParseDecimalOverflow, ParseEmptyDecimalString} from "rain.string/error/ErrParse.sol";
import {LibDecimalFloat, PackedFloat} from "../LibDecimalFloat.sol";
import {LibDecimalFloat, PackedFloat, Float} from "../LibDecimalFloat.sol";
import {LibDecimalFloatImplementation} from "../implementation/LibDecimalFloatImplementation.sol";

library LibParseDecimalFloat {
Expand Down Expand Up @@ -140,4 +140,16 @@ library LibParseDecimalFloat {
}
}
}

function parseDecimalFloat(string memory str) internal pure returns (bytes4 errorSelector, Float memory float) {
uint256 start;
uint256 end;
assembly {
start := add(str, 0x20)
end := add(start, mload(str))
}
uint256 cursor;
(errorSelector, cursor, float.signedCoefficient, float.exponent) = parseDecimalFloat(start, end);
(cursor);
}
}
46 changes: 46 additions & 0 deletions test/src/lib/format/LibFormatDecimalFloat.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 thedavidmeister
pragma solidity =0.8.25;

import {Test} from "forge-std/Test.sol";
import {Float, LibDecimalFloat} from "src/lib/LibDecimalFloat.sol";
import {LibFormatDecimalFloat} from "src/lib/format/LibFormatDecimalFloat.sol";
import {LibParseDecimalFloat} from "src/lib/parse/LibParseDecimalFloat.sol";

/// @title LibFormatDecimalFloatTest
/// @notice Test contract for verifying the functionality of LibFormatDecimalFloat
/// @dev Tests both the stack and memory versions of formatting functions and round-trip conversions
contract LibFormatDecimalFloatTest is Test {
using LibDecimalFloat for Float;
Comment thread
thedavidmeister marked this conversation as resolved.

function toDecimalStringExternal(int256 signedCoefficient, int256 exponent) external pure returns (string memory) {
return LibFormatDecimalFloat.toDecimalString(signedCoefficient, exponent);
}

function toString(Float memory float) external pure returns (string memory) {
return LibFormatDecimalFloat.toDecimalString(float);
}

/// Check that the memory version matches the stack version.
function testFormatMem(Float memory float) external {
try this.toDecimalStringExternal(float.signedCoefficient, float.exponent) returns (string memory formatted) {
string memory actual = this.toString(float);
assertEq(formatted, actual, "Formatted value mismatch");
} catch (bytes memory err) {
vm.expectRevert(err);
LibFormatDecimalFloat.toDecimalString(float);
}
}

/// Test round tripping a value through parse and format.
function testRoundTrip(uint256 value) external pure {
// Dividing by 10 here keeps us clearly within the range of lossless
// conversions.
value = bound(value, 0, type(uint256).max / 10);
Float memory float = LibDecimalFloat.fromFixedDecimalLosslessMem(value, 18);
string memory formatted = LibFormatDecimalFloat.toDecimalString(float);
(bytes4 errorCode, Float memory parsed) = LibParseDecimalFloat.parseDecimalFloat(formatted);
assertEq(errorCode, 0, "Parse error");
assertTrue(float.eq(parsed), "Round trip failed");
}
}
34 changes: 34 additions & 0 deletions test/src/lib/parse/LibParseDecimalFloat.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,45 @@ import {LibBytes, Pointer} from "rain.solmem/lib/LibBytes.sol";
import {Strings} from "openzeppelin-contracts/contracts/utils/Strings.sol";
import {ParseEmptyDecimalString} from "rain.string/error/ErrParse.sol";
import {MalformedExponentDigits, ParseDecimalPrecisionLoss, MalformedDecimalPoint} from "src/error/ErrParse.sol";
import {Float} from "src/lib/LibDecimalFloat.sol";

contract LibParseDecimalFloatTest is Test {
using LibBytes for bytes;
using Strings for uint256;

function parseDecimalFloatExternal(string memory data)
external
pure
returns (bytes4 errorSelector, uint256 cursorAfter, int256 signedCoefficient, int256 exponent)
{
uint256 cursor = Pointer.unwrap(bytes(data).dataPointer());
(errorSelector, cursorAfter, signedCoefficient, exponent) =
LibParseDecimalFloat.parseDecimalFloat(cursor, Pointer.unwrap(bytes(data).endDataPointer()));
}

function parseDecimalFloatExternalMem(string memory data)
external
pure
returns (bytes4 errorSelector, Float memory float)
{
(errorSelector, float) = LibParseDecimalFloat.parseDecimalFloat(data);
}

/// Check that the memory version matches the stack version.
function testParseMem(string memory data) external {
try this.parseDecimalFloatExternal(data) returns (
bytes4 errorSelector, uint256 cursorAfter, int256 signedCoefficient, int256 exponent
) {
(bytes4 errorSelectorMem, Float memory float) = this.parseDecimalFloatExternalMem(data);
assertEq(errorSelector, errorSelectorMem, "Error selector mismatch");
assertEq(signedCoefficient, float.signedCoefficient, "Signed coefficient mismatch");
assertEq(exponent, float.exponent, "Exponent mismatch");
} catch (bytes memory err) {
vm.expectRevert(err);
this.parseDecimalFloatExternalMem(data);
}
}

function checkParseDecimalFloat(
string memory data,
int256 expectedSignedCoefficient,
Expand Down
Loading