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
4 changes: 3 additions & 1 deletion script/Deploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity =0.8.25;

import {Script} from "forge-std/Script.sol";
import {DataContractMemoryContainer, LibDataContract} from "rain.datacontract/lib/LibDataContract.sol";
import {LibDecimalFloatDeploy} from "../src/lib/LibDecimalFloatDeploy.sol";
import {LibDecimalFloatDeploy} from "../src/lib/deploy/LibDecimalFloatDeploy.sol";

contract Deploy is Script {
using LibDataContract for DataContractMemoryContainer;
Expand All @@ -17,6 +17,8 @@ contract Deploy is Script {

container.writeZoltu();

LibDecimalFloatDeploy.decimalFloatZoltu();

vm.stopBroadcast();
}
}
166 changes: 166 additions & 0 deletions src/concrete/DecimalFloat.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

import {LibDecimalFloat, Float} from "../lib/LibDecimalFloat.sol";
import {LOG_TABLES_ADDRESS} from "../lib/deploy/LibDecimalFloatDeploy.sol";
import {LibFormatDecimalFloat} from "../lib/format/LibFormatDecimalFloat.sol";
import {LibParseDecimalFloat} from "../lib/parse/LibParseDecimalFloat.sol";

contract DecimalFloat {
using LibDecimalFloat for Float;

/// Exposes `LibParseDecimalFloat.parseDecimalFloat` for offchain use.
/// @param str The string to parse.
/// @return errorSelector The selector of the error if parsing failed. `0`
/// if parsing succeeded.
/// @return parsed The parsed float. Caller MUST check `errorSelector` to
/// determine if parsing succeeded.
function parse(string memory str) external pure returns (bytes4, Float) {
(bytes4 errorSelector, Float parsed) = LibParseDecimalFloat.parseDecimalFloat(str);
return (errorSelector, parsed);
}

/// Exposes `LibFormatDecimalFloat.toDecimalString` for offchain use.
/// @param a The float to format.
/// @return The string representation of the float.
function format(Float a) external pure returns (string memory) {
return LibFormatDecimalFloat.toDecimalString(a);
}

/// Exposes `LibDecimalFloat.add` for offchain use.
/// @param a The first float to add.
/// @param b The second float to add.
/// @return The sum of the two floats.
function add(Float a, Float b) external pure returns (Float) {
return a.add(b);
}

/// Exposes `LibDecimalFloat.sub` for offchain use.
/// @param a The first float to subtract.
/// @param b The second float to subtract.
/// @return The difference of the two floats.
function sub(Float a, Float b) external pure returns (Float) {
return a.sub(b);
}

/// Exposes `LibDecimalFloat.minus` for offchain use.
/// @param a The float to negate.
/// @return The negated float.
function minus(Float a) external pure returns (Float) {
return a.minus();
}

/// Exposes `LibDecimalFloat.abs` for offchain use.
/// @param a The float to get the absolute value of.
/// @return The absolute value of the float.
function abs(Float a) external pure returns (Float) {
return a.abs();
}

/// Exposes `LibDecimalFloat.mul` for offchain use.
/// @param a The first float to multiply.
/// @param b The second float to multiply.
/// @return The product of the two floats.
function mul(Float a, Float b) external pure returns (Float) {
return a.mul(b);
}

/// Exposes `LibDecimalFloat.div` for offchain use.
/// @param a The first float to divide.
/// @param b The second float to divide.
/// @return The quotient of the two floats.
function div(Float a, Float b) external pure returns (Float) {
return a.div(b);
}

/// Exposes `LibDecimalFloat.inv` for offchain use.
/// @param a The float to invert.
/// @return The inverted float.
function inv(Float a) external pure returns (Float) {
return a.inv();
}

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

/// Exposes `LibDecimalFloat.lt` 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 the second, false otherwise.
function lt(Float a, Float b) external pure returns (bool) {
return a.lt(b);
}

/// Exposes `LibDecimalFloat.gt` 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 the second, false
/// otherwise.
function gt(Float a, Float b) external pure returns (bool) {
return a.gt(b);
}

/// Exposes `LibDecimalFloat.frac` for offchain use.
/// @param a The float to get the fractional part of.
/// @return The fractional part of the float.
function frac(Float a) external pure returns (Float) {
return a.frac();
}

/// Exposes `LibDecimalFloat.floor` for offchain use.
/// @param a The float to get the floor of.
/// @return The floored float.
function floor(Float a) external pure returns (Float) {
return a.floor();
}

/// Exposes `LibDecimalFloat.pow10` for offchain use.
/// @param a The float to raise to the power of 10.
/// @return The result of raising the float to the power of 10.
function pow10(Float a) external view returns (Float) {
return a.pow10(LOG_TABLES_ADDRESS);
}

/// Exposes `LibDecimalFloat.log10` for offchain use.
/// @param a The float to take the logarithm of.
/// @return The logarithm of the float.
function log10(Float a) external view returns (Float) {
return a.log10(LOG_TABLES_ADDRESS);
}

/// Exposes `LibDecimalFloat.pow` for offchain use.
/// @param a The base float.
/// @param b The exponent float.
/// @return The result of raising the base float to the power of the exponent
function pow(Float a, Float b) external view returns (Float) {
return a.pow(b, LOG_TABLES_ADDRESS);
}

/// Exposes `LibDecimalFloat.min` for offchain use.
/// @param a The first float to compare.
/// @param b The second float to compare.
/// @return The smaller of the two floats.
function min(Float a, Float b) external pure returns (Float) {
return a.min(b);
}

/// Exposes `LibDecimalFloat.max` for offchain use.
/// @param a The first float to compare.
/// @param b The second float to compare.
/// @return The larger of the two floats.
function max(Float a, Float b) external pure returns (Float) {
return a.max(b);
}

/// Exposes `LibDecimalFloat.isZero` for offchain use.
/// @param a The float to check.
/// @return True if the float is zero, false otherwise.
function isZero(Float a) external pure returns (bool) {
return a.isZero();
}
}
42 changes: 28 additions & 14 deletions src/lib/LibDecimalFloat.sol
Original file line number Diff line number Diff line change
Expand Up @@ -418,18 +418,32 @@ library LibDecimalFloat {
return result;
}

/// Same as multiply, but accepts a Float struct instead of separate values.
/// Costs more gas but helps mitigate stack depth issues, and is more
/// ergonomic for the caller.
/// https://speleotrove.com/decimal/daops.html#refmult
/// > multiply takes two operands. If either operand is a special value then
/// > the general rules apply.
/// >
/// > Otherwise, the operands are multiplied together
/// > (‘long multiplication’), resulting in a number which may be as long as
/// > the sum of the lengths of the two operands, as follows:
/// >
/// > - The coefficient of the result, before rounding, is computed by
/// > multiplying together the coefficients of the operands.
/// > - The exponent of the result, before rounding, is the sum of the
/// > exponents of the two operands.
/// > - The sign of the result is the exclusive or of the signs of the
/// > operands.
/// >
/// > The result is then rounded to precision digits if necessary, counting
/// > from the most significant digit of the result.
/// @param a The Float struct containing the signed coefficient and
/// exponent of the first floating point number.
/// @param b The Float struct containing the signed coefficient and
/// exponent of the second floating point number.
function multiply(Float a, Float b) internal pure returns (Float) {
function mul(Float a, Float b) internal pure returns (Float) {
(int256 signedCoefficientA, int256 exponentA) = a.unpack();
(int256 signedCoefficientB, int256 exponentB) = b.unpack();
(int256 signedCoefficient, int256 exponent) =
LibDecimalFloatImplementation.multiply(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
LibDecimalFloatImplementation.mul(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
(Float c, bool lossless) = packLossy(signedCoefficient, exponent);
// Multiplication is typically lossless, but can be lossy in edge cases.
(lossless);
Expand All @@ -443,11 +457,11 @@ library LibDecimalFloat {
/// exponent of the first floating point number.
/// @param b The Float struct containing the signed coefficient and
/// exponent of the second floating point number.
function divide(Float a, Float b) internal pure returns (Float) {
function div(Float a, Float b) internal pure returns (Float) {
(int256 signedCoefficientA, int256 exponentA) = a.unpack();
(int256 signedCoefficientB, int256 exponentB) = b.unpack();
(int256 signedCoefficient, int256 exponent) =
LibDecimalFloatImplementation.divide(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
LibDecimalFloatImplementation.div(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
(Float c, bool lossless) = packLossy(signedCoefficient, exponent);
// Division is often lossy because it is very easy to end up with
// infinite decimal representations.
Expand Down Expand Up @@ -539,14 +553,14 @@ library LibDecimalFloat {
/// Same as power10, but accepts a Float struct instead of separate values.
/// Costs more gas but helps mitigate stack depth issues, and is more
/// ergonomic for the caller.
/// @param tablesDataContract The address of the contract containing the
/// logarithm tables.
/// @param float The Float struct containing the signed coefficient and
/// exponent of the floating point number.
function power10(address tablesDataContract, Float float) internal view returns (Float) {
/// @param tablesDataContract The address of the contract containing the
/// logarithm tables.
function pow10(Float float, address tablesDataContract) internal view returns (Float) {
(int256 signedCoefficient, int256 exponent) = float.unpack();
(signedCoefficient, exponent) =
LibDecimalFloatImplementation.power10(tablesDataContract, signedCoefficient, exponent);
LibDecimalFloatImplementation.pow10(tablesDataContract, signedCoefficient, exponent);
(Float result, bool lossless) = packLossy(signedCoefficient, exponent);
// We don't care if power10 is lossy because it's an approximation
// anyway.
Expand Down Expand Up @@ -583,7 +597,7 @@ library LibDecimalFloat {
/// @param b The float `b` in `a^b`.
/// @param tablesDataContract The address of the contract containing the
/// logarithm tables.
function power(Float a, Float b, address tablesDataContract) internal view returns (Float) {
function pow(Float a, Float b, address tablesDataContract) internal view returns (Float) {
(int256 signedCoefficientA, int256 exponentA) = a.unpack();

(int256 signedCoefficientC, int256 exponentC) =
Expand All @@ -592,10 +606,10 @@ library LibDecimalFloat {
(int256 signedCoefficientB, int256 exponentB) = b.unpack();

(signedCoefficientC, exponentC) =
LibDecimalFloatImplementation.multiply(signedCoefficientC, exponentC, signedCoefficientB, exponentB);
LibDecimalFloatImplementation.mul(signedCoefficientC, exponentC, signedCoefficientB, exponentB);

(signedCoefficientC, exponentC) =
LibDecimalFloatImplementation.power10(tablesDataContract, signedCoefficientC, exponentC);
LibDecimalFloatImplementation.pow10(tablesDataContract, signedCoefficientC, exponentC);

(Float c, bool lossless) = packLossy(signedCoefficientC, exponentC);
// We don't care if power is lossy because it's an approximation anyway.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import {
LOG_TABLES_SMALL_ALT,
ANTI_LOG_TABLES,
ANTI_LOG_TABLES_SMALL
} from "../generated/LogTables.pointers.sol";
} from "../../generated/LogTables.pointers.sol";
import {LibDataContract, DataContractMemoryContainer} from "rain.datacontract/lib/LibDataContract.sol";
import {LibBytes} from "rain.solmem/lib/LibBytes.sol";
import {LibMemCpy, Pointer} from "rain.solmem/lib/LibMemCpy.sol";
import {DecimalFloat} from "../../concrete/DecimalFloat.sol";

address constant LOG_TABLES_ADDRESS = 0x7A0D94F55792C434d74a40883C6ed8545E406D12;

library LibDecimalFloatDeploy {
function combinedTables() internal pure returns (bytes memory) {
Expand All @@ -24,4 +27,18 @@ library LibDecimalFloatDeploy {
LibMemCpy.unsafeCopyBytesTo(LibBytes.dataPointer(tables), pointer, tables.length);
return container;
}

function decimalFloatZoltu() internal returns (DecimalFloat deployedAddress) {
//slither-disable-next-line too-many-digits
bytes memory code = type(DecimalFloat).creationCode;
bool success;
assembly ("memory-safe") {
mstore(0, 0)
success := call(gas(), 0x7A0D94F55792C434d74a40883C6ed8545E406D12, 0, add(code, 0x20), mload(code), 12, 20)
deployedAddress := mload(0)
}
Comment thread
thedavidmeister marked this conversation as resolved.
if (!success) {
revert("DecimalFloat: deploy failed");
}
}
}
Loading
Loading