Skip to content

wip on consolidating packed#30

Merged
thedavidmeister merged 13 commits intomainfrom
2025-04-17-consolidate
Apr 28, 2025
Merged

wip on consolidating packed#30
thedavidmeister merged 13 commits intomainfrom
2025-04-17-consolidate

Conversation

@thedavidmeister
Copy link
Copy Markdown
Contributor

@thedavidmeister thedavidmeister commented Apr 18, 2025

Motivation

Solution

Checks

By submitting this for review, I'm confirming I've done the following:

  • made this PR as small as possible
  • unit-tested any new functionality
  • linked any relevant issues or PRs
  • included screenshots (if this involves a front-end change)

Summary by CodeRabbit

  • New Features

    • Introduced a new error for coefficient overflow in decimal float operations.
    • Added comprehensive decimal floating-point arithmetic and logarithmic operations including negation, multiplication, division, addition, subtraction, equality, inversion, base-10 logarithm, and power functions.
  • Refactor

    • Unified the floating point representation to a single packed type, simplifying all related functions and removing the previous struct-based approach.
    • Updated function signatures throughout the codebase and tests to use the new packed representation.
    • Delegated core arithmetic logic to a new implementation library for consistency and reduced duplication.
    • Simplified test contracts by removing redundant external wrappers and memory-based interfaces, standardizing on value-type usage.
    • Replaced direct integer parameter operations with packed float struct methods in tests for consistency and clarity.
    • Extended lookup tables with additional rows to support interpolation beyond previous bounds.
  • Bug Fixes

    • Improved error handling for coefficient overflows, ensuring immediate reverts when limits are exceeded.
  • Tests

    • Updated and refactored all tests to use the new packed float type and adjusted assertions for the new structure.
    • Added extensive new tests for arithmetic operations (add, sub, multiply, divide, inv, eq, log10, power10, minus) validating correctness, edge cases, normalization, overflow handling, and gas usage.
    • Removed obsolete and redundant test cases, focusing on core functionality and equivalence between packed and unpacked representations.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 18, 2025

## Walkthrough

This change refactors the decimal float handling across the codebase to use a single packed `Float` type alias (wrapping `bytes32`) instead of a memory-based `Float` struct. All functions in the main library and related modules now accept and return the packed `Float` type, unpacking it internally as needed. A new custom error, `CoefficientOverflow`, is introduced for coefficient overflows. All test contracts and formatting/parsing utilities are updated to use the packed representation, removing or adapting memory-based code and tests accordingly. Function signatures, parameter passing, and internal logic are aligned with this packed approach. Additionally, the `LibDecimalFloatImplementation` library is extended with comprehensive arithmetic and logarithmic operations, normalization, and overflow checks. Many test contracts are simplified, removing redundant tests and focusing on core equivalence and consistency validations.

## Changes

| Files / Paths                                                         | Change Summary |
|----------------------------------------------------------------------|----------------|
| src/error/ErrDecimalFloat.sol                                        | Added new custom error `CoefficientOverflow(int256 signedCoefficient, int256 exponent)` for coefficient overflow conditions. |
| src/lib/LibDecimalFloat.sol                                          | Replaced `PackedFloat` and memory-based `Float` struct with a packed `Float` type alias; updated all functions to use packed `Float`, added overflow error, removed downscaling on overflow, and unified internal logic by delegating arithmetic to `LibDecimalFloatImplementation`. |
| src/lib/format/LibFormatDecimalFloat.sol                             | Updated `toDecimalString` to accept packed `Float` and use unpacking instead of direct struct field access. |
| src/lib/parse/LibParseDecimalFloat.sol                               | Changed parsing functions to return packed `Float`, updated error handling, removed memory struct usage, lowered fractional precision threshold, and adjusted function signatures accordingly. |
| src/lib/implementation/LibDecimalFloatImplementation.sol             | Added comprehensive arithmetic and logarithmic operations (add, sub, multiply, divide, minus, eq, inv, log10, power10) for decimal floats with normalization, overflow checks, and table-driven approximations. |
| src/lib/table/LibLogTable.sol                                        | Extended fixed-size lookup tables by one row to support interpolation beyond original bounds; adjusted array sizes and assembly loops accordingly; removed console import. |
| test/src/lib/LibDecimalFloat.abs.t.sol<br>test/src/lib/LibDecimalFloat.add.t.sol<br>test/src/lib/LibDecimalFloat.decimal.t.sol<br>test/src/lib/LibDecimalFloat.decimalLossless.t.sol<br>test/src/lib/LibDecimalFloat.divide.t.sol<br>test/src/lib/LibDecimalFloat.eq.t.sol<br>test/src/lib/LibDecimalFloat.floor.t.sol<br>test/src/lib/LibDecimalFloat.frac.t.sol<br>test/src/lib/LibDecimalFloat.gt.t.sol<br>test/src/lib/LibDecimalFloat.inv.t.sol<br>test/src/lib/LibDecimalFloat.log10.t.sol<br>test/src/lib/LibDecimalFloat.lt.t.sol<br>test/src/lib/LibDecimalFloat.max.t.sol<br>test/src/lib/LibDecimalFloat.min.t.sol<br>test/src/lib/LibDecimalFloat.minus.t.sol<br>test/src/lib/LibDecimalFloat.multiply.t.sol<br>test/src/lib/LibDecimalFloat.pack.t.sol<br>test/src/lib/LibDecimalFloat.power.t.sol<br>test/src/lib/LibDecimalFloat.power10.t.sol<br>test/src/lib/LibDecimalFloat.sub.t.sol | Updated all tests to use packed `Float` type, removed or adapted memory-based struct usage, changed function signatures, and updated assertions to use unpacked values; removed redundant external wrappers and many individual test cases, focusing on core equivalence and consistency tests. |
| test/src/lib/format/LibFormatDecimalFloat.t.sol                      | Updated test contract to use packed `Float` type, adjusted function signatures, and replaced memory-based struct handling. |
| test/src/lib/parse/LibParseDecimalFloat.t.sol                        | Updated to use packed `Float`, adapted tests to use unpacking and new function signatures. |
| test/src/lib/implementation/LibDecimalFloatImplementation.add.t.sol | Added new test contract with comprehensive tests for the `add` function in `LibDecimalFloatImplementation`. |
| test/src/lib/implementation/LibDecimalFloatImplementation.divide.t.sol | Added new test contract with multiple division test cases and fuzz tests for `divide` in `LibDecimalFloatImplementation`. |
| test/src/lib/implementation/LibDecimalFloatImplementation.eq.t.sol  | Added new test contract with extensive equality tests for `eq` in `LibDecimalFloatImplementation`. |
| test/src/lib/implementation/LibDecimalFloatImplementation.inv.t.sol | Added new test contract validating the `inv` function against a slow reference implementation. |
| test/src/lib/implementation/LibDecimalFloatImplementation.log10.t.sol | Added new test contract verifying `log10` behavior with exact, lookup, interpolated, and sub-1 cases. |
| test/src/lib/implementation/LibDecimalFloatImplementation.minus.t.sol | Added new test contract verifying `minus` function equivalence to subtraction from zero. |
| test/src/lib/implementation/LibDecimalFloatImplementation.multiply.t.sol | Added new test contract covering multiple multiplication scenarios and property tests for `multiply`. |
| test/src/lib/implementation/LibDecimalFloatImplementation.power10.t.sol | Added new test contract with tests for exact powers, lookups, interpolation, and fuzzing for `power10`. |
| test/src/lib/implementation/LibDecimalFloatImplementation.sub.t.sol  | Added new test contract verifying subtraction behavior and edge cases for `sub`. |
| test/abstract/LogTest.sol                                             | Modified import to also re-export `console2` for convenience. |
| test/lib/LibDecimalFloatSlow.sol                                     | Updated to use `LibDecimalFloatImplementation.divide` in `invSlow` function. |
| test/src/lib/LibDecimalFloat.mixed.t.sol                             | Refactored test code to use `Float` struct methods for division and multiplication instead of raw integer parameters. |
| test/src/lib/LibDecimalFloat.normalize.t.sol                         | Deleted entire contract testing normalization functions. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Caller
    participant LibDecimalFloat
    participant ErrorModule

    Caller->>LibDecimalFloat: pack(signedCoefficient, exponent)
    LibDecimalFloat->>LibDecimalFloat: check coefficient fits 224 bits
    alt Overflow
        LibDecimalFloat->>ErrorModule: revert CoefficientOverflow(signedCoefficient, exponent)
    else No Overflow
        LibDecimalFloat->>LibDecimalFloat: pack into Float (bytes32)
        LibDecimalFloat-->>Caller: Float
    end

Possibly related PRs

  • rainlanguage/rain.math.float#25: Adds min and max functions and tests, related to this PR’s updates on decimal float operations and test simplifications.
  • rainlanguage/rain.math.float#26: Introduces initial LibFormatDecimalFloat library with toDecimalString using memory structs, related to this PR’s update to use packed Float and unpacking.
  • rainlanguage/rain.math.float#28: Modifies decimal float packing to increase coefficient bit width, related to this PR’s packed float refactor and overflow error.

Suggested reviewers

  • hardyjosh


<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 1b1693586da549f07c7d1f4edf8c8b510083f8ce and 9ed774e3a1d39fef22fa465c77d83afb5d2ad34d.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `.gas-snapshot` (1 hunks)
* `test/src/lib/LibDecimalFloat.power.t.sol` (2 hunks)

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>test/src/lib/LibDecimalFloat.power.t.sol (1)</summary>

`13-15`: **`diffLimit` helper looks good**

Returning `0.094` via `packLossless(94, -3)` is clear and keeps the tolerance centralised.

</details>

</blockquote></details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=rainlanguage/rain.math.float&utm_content=30):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🔭 Outside diff range comments (10)
test/src/lib/LibDecimalFloat.inv.t.sol (1)

20-32: ⚠️ Potential issue

Test failure detected: ExponentOverflow error needs addressing.

The updated test correctly unpacks and compares Float components, but pipeline failures indicate an ExponentOverflow(-5e37, -2.147e9) error is occurring during testing.

The test implementation is correct, but the pipeline error suggests that there may be:

  1. A limitation in the new packed representation
  2. An edge case in the inv function when handling extreme values
  3. A need for additional bounds checking before calling inv

Consider adding appropriate input bounds checking or handling for extreme exponent values in the testInvMem function.

test/src/lib/LibDecimalFloat.log10.t.sol (1)

23-36: ⚠️ Potential issue

Test failure detected: CoefficientOverflow error needs addressing.

The test correctly unpacks and compares Float components, but pipeline failures indicate a CoefficientOverflow(5e69, -36) error is occurring.

The test implementation follows the same pattern as other updated tests, but the pipeline error suggests:

  1. The input values may be causing coefficient overflow in the new packed representation
  2. Additional bounds checking may be needed in the testLog10Mem function

Consider adding appropriate input bounds checking or handling for large coefficient values to ensure compatibility with the packed Float representation.

test/src/lib/LibDecimalFloat.power10.t.sol (1)

21-34: ⚠️ Potential issue

Fix ExponentOverflow in testPower10Mem

The pipeline is failing with an ExponentOverflow error in this function. This occurs when the exponent exceeds the allowed range in the packed representation.

#!/bin/bash
# Analyze how the ExponentOverflow is occurring in the test

# Look at the power10 implementation and test function
rg -A 5 "ExponentOverflow"
cat test/src/lib/LibDecimalFloat.power10.t.sol | grep -A 15 "testPower10Mem"

The test needs additional bounds checking on the input float to prevent exponent overflow:

 function testPower10Mem(Float float) external {
+    // Avoid excessively large exponents that cause overflow
+    (int256 signedCoeff, int256 exp) = float.unpack();
+    exp = bound(exp, -70, 70);
+    float = LibDecimalFloat.pack(signedCoeff, exp);
     (int256 signedCoefficientFloat, int256 exponentFloat) = float.unpack();
test/src/lib/LibDecimalFloat.multiply.t.sol (2)

33-47: ⚠️ Potential issue

Fix CoefficientOverflow in testMultiplyMem

The pipeline is failing with a CoefficientOverflow error in this function. This occurs when the multiplication result exceeds the coefficient capacity in the packed representation.

#!/bin/bash
# Analyze multiplication coefficient overflow issues

# Look at multiply implementation
rg -A 10 "multiply.*Float" src/

The test needs additional bounds on the input values to prevent coefficient overflow during multiplication:

 function testMultiplyMem(Float a, Float b) external {
+    // Bound coefficients to prevent overflow in multiplication
+    (int256 aCoeff, int256 aExp) = a.unpack();
+    (int256 bCoeff, int256 bExp) = b.unpack();
+    aCoeff = bound(aCoeff, -1e30, 1e30);
+    bCoeff = bound(bCoeff, -1e30, 1e30);
+    a = LibDecimalFloat.pack(aCoeff, aExp);
+    b = LibDecimalFloat.pack(bCoeff, bExp);
     (int256 signedCoefficientA, int256 exponentA) = a.unpack();

126-141: 🧹 Nitpick (assertive)

Consider updating fuzzing test for packed Float representation

This fuzzing test should also be updated to work with the packed Float representation to ensure comprehensive testing of the new implementation.

 function testMultiplyNotRevertAnyExpectation(
-    int256 signedCoefficientA,
-    int256 exponentA,
-    int256 signedCoefficientB,
-    int256 exponentB
+    Float a,
+    Float b
 ) external pure {
+    (int256 signedCoefficientA, int256 exponentA) = a.unpack();
+    (int256 signedCoefficientB, int256 exponentB) = b.unpack();
     exponentA = bound(exponentA, EXPONENT_MIN, EXPONENT_MAX);
     exponentB = bound(exponentB, EXPONENT_MIN, EXPONENT_MAX);
+    
+    a = LibDecimalFloat.pack(signedCoefficientA, exponentA);
+    b = LibDecimalFloat.pack(signedCoefficientB, exponentB);
+    
+    Float result = LibDecimalFloat.multiply(a, b);
+    (int256 signedCoefficient, int256 exponent) = result.unpack();
-    (int256 signedCoefficient, int256 exponent) =
-        LibDecimalFloat.multiply(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
     (int256 expectedSignedCoefficient, int256 expectedExponent) =
         LibDecimalFloatSlow.multiplySlow(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
test/src/lib/LibDecimalFloat.divide.t.sol (1)

24-38: ⚠️ Potential issue

Renamed test function and associated ExponentOverflow failure.

The test was appropriately renamed from testDivideMem to testDividePacked to reflect its use of packed values, but it's failing with ExponentOverflow(1e38, 2.454e9). This suggests the refactored version has stricter exponent validation than before.

You need to either:

  1. Modify the fuzz tests to bound inputs within exponent limits
  2. Add exception handling for this specific overflow case
 function testDividePacked(Float a, Float b) external {
+    // Skip test if inputs would cause exponent overflow
+    (int256 signedCoefficientA, int256 exponentA) = a.unpack();
+    (int256 signedCoefficientB, int256 exponentB) = b.unpack();
+    if (exponentA > 1e8 || exponentB > 1e8) return;
+    
     (int256 signedCoefficientA, int256 exponentA) = a.unpack();
     (int256 signedCoefficientB, int256 exponentB) = b.unpack();
test/src/lib/LibDecimalFloat.floor.t.sol (1)

1-113: 🧹 Nitpick (assertive)

Consider adding bound checks for all fuzzing tests.

Since other similar tests are failing with overflow errors, it might be valuable to add appropriate bounds for this test as well to prevent similar issues in the future.

 function testFloorMem(Float float) external {
+    // Skip test cases that might cause coefficient or exponent overflow
+    (int256 signedCoefficient, int256 exponent) = float.unpack();
+    if (exponent > 1e8 || exponent < -1e8) return;
+    
     (int256 signedCoefficient, int256 exponent) = float.unpack();
test/src/lib/LibDecimalFloat.pack.t.sol (1)

19-28: 💡 Verification agent

🧩 Analysis chain

Fix failing test due to incorrect exponent handling for zero

The test is failing with "exponent mismatch: 1 != 0" because the test is not handling zero coefficients correctly.

When the coefficient is zero, the test expects the exponent to be zero regardless of what was packed. Check the implementation of unpack to see how it normalizes zero values:

 function testPartsRoundTrip(int224 signedCoefficient, int32 exponent) external pure {
+    // Special handling for zero - we know exponent will always be normalized to 0
+    if (signedCoefficient == 0) {
+        exponent = 0;
+    }
+
     Float float = LibDecimalFloat.pack(signedCoefficient, exponent);
     (int256 signedCoefficientOut, int256 exponentOut) = LibDecimalFloat.unpack(float);

     assertEq(signedCoefficient, signedCoefficientOut, "coefficient");
     if (signedCoefficient != 0) {
         assertEq(exponent, exponentOut, "exponent");
     } else {
         // 0 exponent is always 0.
         assertEq(exponentOut, 0, "exponent");
     }

🏁 Script executed:

#!/bin/bash
# Check how the unpack function handles zero coefficients
ast-grep --pattern $'function unpack(Float float) internal pure returns'

Length of output: 73


🏁 Script executed:

#!/bin/bash
set -e
# Locate the LibDecimalFloat library file
file=$(rg -l "library LibDecimalFloat" -t sol)
echo "Library file: $file"

echo "----- pack() implementation -----"
rg -n "function pack" -A20 "$file"

echo "----- unpack() implementation -----"
rg -n "function unpack" -A20 "$file"

Length of output: 86


🏁 Script executed:

#!/bin/bash
set -e

# List all Solidity files to locate the library
echo "Solidity files in repo:"
fd --extension sol

# Search for the LibDecimalFloat library definition
lib_file=$(rg -l "library LibDecimalFloat" --glob "*.sol")
echo "Found LibDecimalFloat file: $lib_file"

# Show the pack() implementation
echo "----- pack() implementation -----"
rg -n "function pack" -A20 "$lib_file"

# Show the unpack() implementation
echo "----- unpack() implementation -----"
rg -n "function unpack" -A20 "$lib_file"

Length of output: 3140


🏁 Script executed:

#!/bin/bash
set -e

echo "----- function pack definitions -----"
rg -n "function pack" -g "src/lib/*.sol" -g "src/lib/implementation/*.sol" || echo "No pack definitions found in these paths."

echo "----- function unpack definitions -----"
rg -n "function unpack" -g "src/lib/*.sol" -g "src/lib/implementation/*.sol" || echo "No unpack definitions found in these paths."

Length of output: 602


🏁 Script executed:

#!/bin/bash
set -e

# Display pack implementation with context
echo "----- pack() implementation (lines around 304) -----"
rg -n -C5 "function pack" src/lib/LibDecimalFloat.sol

# Display unpack implementation with context
echo "----- unpack() implementation (lines around 329) -----"
rg -n -C5 "function unpack" src/lib/LibDecimalFloat.sol

Length of output: 1570


Align test with actual pack/unpack behavior: always assert exponent matches

The current implementation of pack/unpack preserves the exponent even when the coefficient is zero. The test should therefore always compare the unpacked exponent to the input exponent instead of special‑casing zero.

• File: test/src/lib/LibDecimalFloat.pack.t.sol
Update the testPartsRoundTrip function (around lines 19–28) as follows:

-    if (signedCoefficient != 0) {
-        assertEq(exponent, exponentOut, "exponent");
-    } else {
-        // 0 exponent is always 0.
-        assertEq(exponentOut, 0, "exponent");
-    }
+    // Exponent is preserved by unpack(), even for zero coefficients
+    assertEq(exponent, exponentOut, "exponent");

This change removes the incorrect zero‑coefficient branch and ensures the test matches the library’s behavior.

src/lib/LibDecimalFloat.sol (2)

304-318: 🛠️ Refactor suggestion

pack can be micro‑optimised and made safer

  1. Branchless masking of the exponent is cheaper than an int32 cast/revert, and avoids silent truncation if the cast is ever removed.
  2. The current mask/OR works but allocates a local mask every call; consider an immutable constant.
+uint256 constant COEFF_MASK = type(uint224).max;
+uint256 constant EXP_SHIFT  = 224;

 function pack(int256 signedCoefficient, int256 exponent) internal pure returns (Float float) {
-    if (int224(signedCoefficient) != signedCoefficient) {
+    if (signedCoefficient < type(int224).min || signedCoefficient > type(int224).max) {
         revert CoefficientOverflow(signedCoefficient, exponent);
     }
-    if (int32(exponent) != exponent) {
+    if (exponent < type(int32).min || exponent > type(int32).max) {
         revert ExponentOverflow(signedCoefficient, exponent);
     }
-    uint256 mask = type(uint224).max;
     assembly ("memory-safe") {
-        float := or(and(signedCoefficient, mask), shl(0xe0, exponent))
+        float := or(and(signedCoefficient, COEFF_MASK), shl(EXP_SHIFT, exponent))
     }
 }

128-140: 💡 Verification agent

🧩 Analysis chain

Doc‑strings still refer to a “Float struct”

After migrating to the packed type Float is bytes32, the NatSpec comments referencing “Float struct” (e.g. “returns a Float struct containing …”) are misleading.

Please update all affected comments (fromFixedDecimal*, toFixedDecimal*, etc.) to avoid confusion for integrators and automated documentation generators.


🏁 Script executed:

#!/bin/bash
# Search for doc-comments referencing "Float struct" across the codebase
rg -n "Float struct" src
# Search for any @return tags mentioning "Float"
rg -n "@return.*Float" src

Length of output: 5958


Update NatSpec comments to reflect packed Float type (bytes32)

The documentation still refers to a non‑existent “Float struct” even though Float is now a packed alias over bytes32. Please update all NatSpec references in src/lib/LibDecimalFloat.sol to remove “struct” language and simply refer to the Float type. For example:

  • Change

/// @return float The Float struct containing the signed coefficient and exponent.

to  

/// @return float The Float containing the signed coefficient and exponent.

- Change  

/// @param float The Float struct containing the signed coefficient and ...

to  

/// @param float The Float containing the signed coefficient and …


Affected sections include, but aren’t limited to:  
- `fromFixedDecimal*` / `toFixedDecimal*` conversions  
- Arithmetic methods (`add`, `sub`, `multiply`, `divide`, etc.)  
- Comparison methods (`eq`, `lt`, `gt`, etc.)  
- Utility methods (`abs`, `minus`, `power10`, `inv`, etc.)

Ensuring these comments accurately describe the packed `Float` type will improve clarity for integrators and automated documentation generators.

</blockquote></details>

</blockquote></details>

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>test/src/lib/LibDecimalFloat.min.t.sol (1)</summary><blockquote>

`24-33`: **Same `expectRevert` positioning issue as in the max test**

Move `vm.expectRevert(err)` *before* the second call, or assert the revert reason from the first call directly (see comment in `max.t.sol`).

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 23112899eb095001e9bdfd829cfe9149ea232d7a and f37077005380c617e76afea17822b8424c3b536d.

</details>

<details>
<summary>📒 Files selected for processing (27)</summary>

* `src/error/ErrDecimalFloat.sol` (1 hunks)
* `src/lib/LibDecimalFloat.sol` (27 hunks)
* `src/lib/format/LibFormatDecimalFloat.sol` (1 hunks)
* `src/lib/parse/LibParseDecimalFloat.sol` (3 hunks)
* `test/src/lib/LibDecimalFloat.abs.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.add.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.decimal.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.decimalLossless.t.sol` (3 hunks)
* `test/src/lib/LibDecimalFloat.divide.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.eq.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.floor.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.frac.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.gt.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.inv.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.log10.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.lt.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.max.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.min.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.minus.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.multiply.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.normalize.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.pack.t.sol` (2 hunks)
* `test/src/lib/LibDecimalFloat.power.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.power10.t.sol` (1 hunks)
* `test/src/lib/LibDecimalFloat.sub.t.sol` (1 hunks)
* `test/src/lib/format/LibFormatDecimalFloat.t.sol` (2 hunks)
* `test/src/lib/parse/LibParseDecimalFloat.t.sol` (3 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Actions: Rainix CI</summary>

<details>
<summary>test/src/lib/LibDecimalFloat.power.t.sol</summary>

[error] 1-1: Test 'testPowerMem(bytes32,bytes32)' failed due to ExponentOverflow(6683, 8.745e18).

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.decimal.t.sol</summary>

[error] 1-1: Test 'testFromFixedDecimalLossyPacked(uint256,uint8)' failed due to CoefficientOverflow(1.347e67, 0).

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.divide.t.sol</summary>

[error] 1-1: Test 'testDividePacked(bytes32,bytes32)' failed due to ExponentOverflow(1e38, 2.454e9).

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.sub.t.sol</summary>

[error] 1-1: Test 'testSubMem(bytes32,bytes32)' failed due to CoefficientOverflow(-9.999e70, -34).

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.multiply.t.sol</summary>

[error] 1-1: Test 'testMultiplyMem(bytes32,bytes32)' failed due to CoefficientOverflow(1.348e67, 0).

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.log10.t.sol</summary>

[error] 1-1: Test 'testLog10Mem(bytes32)' failed due to CoefficientOverflow(5e69, -36).

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.inv.t.sol</summary>

[error] 1-1: Test 'testInvMem(bytes32)' failed due to ExponentOverflow(-5e37, -2.147e9).

</details>
<details>
<summary>test/src/lib/format/LibFormatDecimalFloat.t.sol</summary>

[error] 1-1: Test 'testRoundTrip(uint256)' failed due to CoefficientOverflow(1.347e67, -18).

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.power10.t.sol</summary>

[error] 1-1: Test 'testPower10Mem(bytes32)' failed due to ExponentOverflow(1000, 1.422e66).

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.decimalLossless.t.sol</summary>

[error] 1-1: Test 'testFromFixedDecimalLosslessMem(uint256,uint8)' failed due to CoefficientOverflow(1.347e67, 0).

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.pack.t.sol</summary>

[error] 1-1: Test 'testPartsRoundTrip(int224,int32)' failed due to exponent mismatch: 1 != 0.

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.add.t.sol</summary>

[error] 1-1: Test 'testAddPacked(bytes32,bytes32)' failed due to CoefficientOverflow(1e70, -34).

---

[error] 1-1: Test 'testAddUnpacked(int256,int256,int256,int256)' failed due to ExponentOverflow(0, 2.147e9).

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (48)</summary><blockquote>

<details>
<summary>src/error/ErrDecimalFloat.sol (1)</summary>

`5-6`: **New error type adds proper overflow handling for coefficients**

The addition of `CoefficientOverflow` error follows the same pattern as the existing `ExponentOverflow` error. This enhances error handling specificity for decimal float operations.

</details>
<details>
<summary>src/lib/format/LibFormatDecimalFloat.sol (1)</summary>

`23-25`: **Good adaption to packed Float representation**

Function properly modified to use the packed `Float` type, unpacking it with `LibDecimalFloat.unpack()` instead of directly accessing struct fields. This is consistent with the codebase-wide transition to using packed representations.

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.frac.t.sol (2)</summary>

`15-17`: **Correct adoption of packed Float type**

Function signature properly updated to use packed `Float` type by value rather than `Float memory`, aligning with the codebase-wide changes to use packed representation.

---

`19-31`: **Test correctly adapted for packed Float values**

Test function properly updated to:
1. Unpack the input Float to get components
2. Compare against unpacked values from the result
3. Maintain the same try-catch error handling logic

This maintains test integrity while adopting the new packed Float representation.

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.minus.t.sol (2)</summary>

`16-18`: **Correct adoption of packed Float type**

Function signature properly updated to use packed `Float` type by value rather than `Float memory`, aligning with the codebase-wide changes to use packed representation.

---

`21-33`: **Test correctly adapted for packed Float values**

Test function properly updated to:
1. Unpack the input Float to get components
2. Compare against unpacked values from the result
3. Maintain the same try-catch error handling logic

This maintains test integrity while adopting the new packed Float representation.

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.lt.t.sol (2)</summary>

`21-23`: **Good addition of a wrapper function for testing the packed Float representation.**

The new `ltExternal` function provides a clear way to test the packed `Float` version of the `lt` function. This overloaded function helps maintain test coverage while transitioning to the packed representation.

---

`25-35`: **Well-structured update to test the packed Float type.**

This test implementation now properly:
1. Unpacks the input `Float` values to get their components
2. Tests both the unpacked component version and the packed version
3. Maintains error handling behavior consistency

Good use of the `unpack()` method to facilitate the comparison between the two function variants.

</details>
<details>
<summary>test/src/lib/parse/LibParseDecimalFloat.t.sol (4)</summary>

`11-11`: **Good update to import the required library for Float type.**

The import correctly brings in the `Float` type from `LibDecimalFloat` to support the packed representation changes.

---

`16-16`: **Correct addition of the using directive for LibDecimalFloat.**

Adding `using LibDecimalFloat for Float` properly enables access to the `unpack()` method needed for the updated test implementation.

---

`28-34`: **Well-named function change reflecting packed representation.**

Renaming from `parseDecimalFloatExternalMem` to `parseDecimalFloatExternalPacked` clearly communicates the change in representation, and the return type is correctly updated to use the packed `Float` type.

---

`42-50`: **Properly updated test to handle the packed Float representation.**

The test now:
1. Calls the packed version of the parse function
2. Correctly verifies error selectors match
3. Unpacks the returned Float to compare individual components
4. Maintains error handling behavior consistency

Good job ensuring backward compatibility with the test while adapting to the new type system.

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.inv.t.sol (1)</summary>

`15-17`: **Correctly updated function signature for packed Float.**

The `invExternal` function now accepts and returns the packed `Float` type without the memory modifier, aligning with the consolidated representation changes.

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.log10.t.sol (1)</summary>

`17-20`: **Correctly updated function signature for packed Float.**

The `log10External` function now accepts and returns the packed `Float` type without the memory modifier, aligning with the consolidated representation changes.

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.gt.t.sol (2)</summary>

`21-22`: **LGTM - Float passing by value instead of memory.**

This new overload properly handles the packed `Float` values directly, aligning with the codebase refactor to standardize on a single packed representation.

---

`26-29`: **Correct implementation of unpacking in test.**

The function correctly unpacks Float values into their components before passing to the component-based implementation, allowing for proper comparison of packed versus unpacked implementations.

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.normalize.t.sol (2)</summary>

`15-16`: **LGTM - Consistent with packed Float refactor.**

The function signature properly handles the packed `Float` type by value rather than by memory reference.

---

`20-28`: **Correct approach to testing normalized values.**

The implementation properly unpacks the input Float, calls the component-wise normalization, and then compares against the packed version. The assertions are updated to correctly compare the normalized unpacked values with the corresponding components of the normalized packed Float.

</details>
<details>
<summary>src/lib/parse/LibParseDecimalFloat.sol (6)</summary>

`16-16`: **LGTM - Updated import for consolidated Float type.**

Import has been properly updated to the new consolidated Float type from LibDecimalFloat.

---

`20-20`: **LGTM - Return type updated to Float from PackedFloat.**

Function signature properly updated to return the unified `Float` type rather than the previous `PackedFloat` type.

---

`24-24`: **LGTM - Using Float.wrap for error cases.**

Error cases now return a wrapped zero value for the packed Float type.

---

`27-27`: **LGTM - Using LibDecimalFloat.pack consistently.**

Return statement correctly uses the pack function to create a Float from its components.

---

`128-128`: **LGTM - Simplified function signature.**

Function now returns `Float` directly rather than `Float memory`, consistent with the packed representation.

---

`135-138`: **Unused variable and simplified implementation.**

The `cursor` variable is assigned but not used in the return statement. The code has been simplified to avoid unnecessary variable declarations.

</details>
<details>
<summary>test/src/lib/LibDecimalFloat.eq.t.sol (2)</summary>

`21-22`: **LGTM - Float passing by value.**

This new overload properly handles the packed `Float` values directly, aligning with the codebase refactor.

---

`25-28`: <details>
<summary>✅ Verification successful</summary>

**Function renamed from testEqMem to testEqPacked with correct implementation.**

The test function has been renamed from `testEqMem` to `testEqPacked` to better reflect its use of packed Float values. The implementation correctly unpacks Float values into their components before passing to the component-based implementation.


---

🏁 Script executed:

```shell
#!/bin/bash
# Check for any references to the old function name that might need to be updated
rg "testEqMem" --glob "*.sol" --glob "*.md" --glob "*.js" --glob "*.ts"

Length of output: 71


Rename Verified: No Residual References to testEqMem

All occurrences of the old testEqMem name have been removed, and the new testEqPacked function correctly unpacks both Float values before delegating to eqExternal. Ready to merge.

test/src/lib/format/LibFormatDecimalFloat.t.sol (2)

20-22: Parameter type updated to use packed Float

The toString function has been correctly updated to use the packed Float type by value instead of a memory struct. This aligns with the global refactoring to use a single packed representation.


25-34: Unpacking strategy correctly implemented

The testFormatMem function now properly unpacks the Float value into its components before calling the external function. The use of explicit unpacking replaces the previous direct struct field access.

test/src/lib/LibDecimalFloat.abs.t.sol (2)

15-17: Parameter type updated to use packed Float

The absExternal function signature has been correctly updated to use the packed Float type by value instead of a memory struct. This aligns with the global refactoring to use a single packed representation.


19-28: Test function renamed and updated to use unpacking

The test has been appropriately renamed from testAbsMem to testAbsPacked to reflect the new underlying representation. The implementation correctly unpacks values using LibDecimalFloat.unpack() and compares the raw bytes using Float.unwrap() for proper equality testing.

test/src/lib/LibDecimalFloat.power10.t.sol (1)

15-18: Parameter type updated to use packed Float

The power10External function has been correctly updated to use the packed Float type by value instead of a memory struct. This aligns with the global refactoring to use a single packed representation.

test/src/lib/LibDecimalFloat.multiply.t.sol (1)

28-30: Parameter type updated to use packed Float

The multiplyExternal function has been correctly updated to use the packed Float type by value instead of a memory struct. This aligns with the global refactoring to use a single packed representation.

test/src/lib/LibDecimalFloat.power.t.sol (1)

20-22: Good addition of value-type interface.

The new powerExternal function that accepts and returns packed Float values aligns with the PR's goal of consolidating to a packed representation.

test/src/lib/LibDecimalFloat.divide.t.sol (1)

20-22: Good addition of value-type interface.

The new divideExternal function for packed Float values is consistent with the consolidation pattern used throughout the codebase.

test/src/lib/LibDecimalFloat.sub.t.sol (2)

20-22: Good addition of value-type interface.

The new subExternal function using the packed Float type is consistent with the overall refactoring approach.


25-38:

Details

❓ Verification inconclusive

Test failing with CoefficientOverflow.

The test implementation correctly unpacks and compares component values, but it's failing with CoefficientOverflow(-9.999e70, -34). This suggests the new packed implementation has stricter coefficient validation than before.


🏁 Script executed:

#!/bin/bash
# Find where CoefficientOverflow is defined and used
rg -A 3 "CoefficientOverflow" --glob "*.sol"

# Check the coefficient bit size limitations in LibDecimalFloat
rg "coefficient.*bit" --glob "*LibDecimalFloat.sol"

# Look for functions that might be handling coefficient bounds
rg "coefficientMax|maxCoefficient" --glob "*.sol"

Length of output: 1117


🏁 Script executed:

#!/bin/bash
# Locate the subExternal overloads in LibDecimalFloat.sol
rg -A 5 "function subExternal" --glob "src/lib/LibDecimalFloat.sol"

Length of output: 67


🏁 Script executed:

#!/bin/bash
# Search for subExternal definitions across the repo
rg -n "subExternal" --glob "*.sol"

Length of output: 631


🏁 Script executed:

#!/bin/bash
# Search for pack and unpack functions in the repository
rg -n "pack(" --glob "*.sol"
rg -n "unpack(" --glob "*.sol"

Length of output: 189


Verify coefficient bit‑size constraints causing overflow
The test value Float(-9.999e70, -34) appears to exceed the packed coefficient limit in LibDecimalFloat.sol. The implementation reserves 32 bits for the exponent (int32) and the remaining 224 bits for the signed coefficient, so any absolute coefficient > 2^223 − 1 will trigger CoefficientOverflow.

Please confirm the limits in:

  • src/error/ErrDecimalFloat.sol: error CoefficientOverflow(int256 signedCoefficient, int256 exponent);
  • src/lib/LibDecimalFloat.sol: the packing logic and coefficient bound checks around where it reverts on overflow

Then adjust the test inputs or expectations (e.g. expect a revert) to align with these constraints.

test/src/lib/LibDecimalFloat.floor.t.sol (2)

15-17: Good addition of value-type interface.

The new floorExternal function that accepts and returns packed Float values aligns with the consolidated representation approach.


20-33: Test updated correctly.

This test has been properly updated to handle the packed Float type by unpacking values and comparing components individually. Unlike the other tests, this one doesn't appear to have any pipeline failures.

test/src/lib/LibDecimalFloat.add.t.sol (1)

20-22: LGTM - Function signature updated to use packed Float type

The function now accepts and returns the packed Float type instead of unpacked components, aligning with the codebase consolidation around packed representations.

test/src/lib/LibDecimalFloat.decimal.t.sol (2)

27-29: LGTM - Function updated to use packed Float type

Function signature properly updated to accept a packed Float value instead of separate components.


44-57: LGTM - Test correctly updated to use packed Float

This test properly unpacks the Float value and tests both packed and unpacked paths, with appropriate error handling.

test/src/lib/LibDecimalFloat.pack.t.sol (4)

4-4: LGTM - Import updated to remove reference to PackedFloat

The import statement has been correctly updated to remove PackedFloat and other unused imports, aligning with the overall refactoring.


9-11: LGTM - Function updated to use packed Float type

Function signature correctly updated to return Float directly instead of PackedFloat.


13-15: LGTM - Function updated to use packed Float type

Function parameter correctly updated to accept Float directly instead of PackedFloat.


37-39: LGTM - Test updated to use packed Float type

Test correctly updated to use the consolidated Float type instead of PackedFloat.

test/src/lib/LibDecimalFloat.decimalLossless.t.sol (3)

21-23: LGTM - Function updated to use packed Float type

Function signature properly updated to return packed Float directly.


33-35: LGTM - Function updated to use packed Float type

Function signature properly updated to accept packed Float by value.


53-63: LGTM - Test updated to use packed Float type

Test correctly updated to use the packed Float type, with proper unpacking and error handling.

Comment thread src/lib/parse/LibParseDecimalFloat.sol
Comment thread test/src/lib/format/LibFormatDecimalFloat.t.sol
Comment on lines 25 to 35
function testPowerMem(Float a, Float b) external {
(int256 signedCoefficientA, int256 exponentA) = a.unpack();
(int256 signedCoefficientB, int256 exponentB) = b.unpack();
try this.powerExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB) returns (
int256 signedCoefficient, int256 exponent
) {
Float memory float = this.powerExternal(a, b);
assertEq(signedCoefficient, float.signedCoefficient);
assertEq(exponent, float.exponent);
Float float = this.powerExternal(a, b);
(int256 signedCoefficientUnpacked, int256 exponentUnpacked) = float.unpack();
assertEq(signedCoefficient, signedCoefficientUnpacked);
assertEq(exponent, exponentUnpacked);
} catch (bytes memory err) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Test failures due to ExponentOverflow condition.

The refactored test properly unpacks values and compares components individually, but it's failing with ExponentOverflow(6683, 8.745e18) according to pipeline failures. This suggests the packed implementation has stricter validation for exponent ranges.


🏁 Script executed:

#!/bin/bash
# Find more instances where ExponentOverflow occurs in tests
rg -A 2 "ExponentOverflow" --glob "*.sol"

# Check where exponent bounds are defined in the codebase
rg "EXPONENT_(MIN|MAX)" --glob "*.sol"

Length of output: 9517


I need to inspect the exponent‐bound constants and the power/powerExternal implementations in src/lib/LibDecimalFloat.sol to confirm where the tighter overflow check is coming from. I'll gather those details next.


I need to inspect the powerExternal wrapper and the underlying power implementation to see where the exponent check is applied. Let me pull those definitions.


🏁 Script executed:

#!/bin/bash
# Show the external wrapper in the main library
rg -n -C 5 "powerExternal" src/lib/LibDecimalFloat.sol

# Show the internal power logic in the implementation
rg -n -C 5 "function power" src/lib/implementation/LibDecimalFloatImplementation.sol

Length of output: 141


Handle ExponentOverflow in testPowerMem
The power implementation now reverts with ExponentOverflow whenever the computed exponent (exponentA * signedCoefficientB) exceeds EXPONENT_MAX. Your testPowerMem must explicitly assert that revert and return early.

• File: test/src/lib/LibDecimalFloat.power.t.sol
In function testPowerMem, replace the empty catch block with:

     } catch (bytes memory err) {
-        // currently unhandled revert
+        // Expect ExponentOverflow for inputs that overflow the exponent range
+        assertEq(abi.decode(err, (bytes4)), ExponentOverflow.selector);
+        return;
     }

• (Optional) After the try/catch, add a revert() or fail() to catch any unexpected code paths.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function testPowerMem(Float a, Float b) external {
(int256 signedCoefficientA, int256 exponentA) = a.unpack();
(int256 signedCoefficientB, int256 exponentB) = b.unpack();
try this.powerExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB) returns (
int256 signedCoefficient, int256 exponent
) {
Float memory float = this.powerExternal(a, b);
assertEq(signedCoefficient, float.signedCoefficient);
assertEq(exponent, float.exponent);
Float float = this.powerExternal(a, b);
(int256 signedCoefficientUnpacked, int256 exponentUnpacked) = float.unpack();
assertEq(signedCoefficient, signedCoefficientUnpacked);
assertEq(exponent, exponentUnpacked);
} catch (bytes memory err) {
function testPowerMem(Float a, Float b) external {
(int256 signedCoefficientA, int256 exponentA) = a.unpack();
(int256 signedCoefficientB, int256 exponentB) = b.unpack();
try this.powerExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB) returns (
int256 signedCoefficient, int256 exponent
) {
Float float = this.powerExternal(a, b);
(int256 signedCoefficientUnpacked, int256 exponentUnpacked) = float.unpack();
assertEq(signedCoefficient, signedCoefficientUnpacked);
assertEq(exponent, exponentUnpacked);
} catch (bytes memory err) {
// Expect ExponentOverflow for inputs that overflow the exponent range
assertEq(abi.decode(err, (bytes4)), ExponentOverflow.selector);
return;
}
}

Comment thread test/src/lib/LibDecimalFloat.add.t.sol Outdated
Comment on lines +38 to +51
function testAddUnpacked(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
external
{
try this.addExternal(
LibDecimalFloat.pack(signedCoefficientA, exponentA), LibDecimalFloat.pack(signedCoefficientB, exponentB)
) returns (Float resultPacked) {
(int256 signedCoefficient, int256 exponent) =
this.addExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
assertEq(Float.unwrap(resultPacked), Float.unwrap(LibDecimalFloat.pack(signedCoefficient, exponent)));
} catch (bytes memory err) {
vm.expectRevert(err);
this.addExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix failing test to handle exponent overflow

This new test properly covers both packed and unpacked addition paths, but it's failing due to exponent overflow issues.

The test is failing with ExponentOverflow(0, 2.147e9) which suggests the exponent values need to be bounded.

 function testAddUnpacked(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
     external
 {
+    // Avoid exponent overflow errors
+    exponentA = bound(exponentA, EXPONENT_MIN, EXPONENT_MAX);
+    exponentB = bound(exponentB, EXPONENT_MIN, EXPONENT_MAX);
+    // Avoid coefficient overflow errors
+    signedCoefficientA = bound(signedCoefficientA, type(int224).min, type(int224).max);
+    signedCoefficientB = bound(signedCoefficientB, type(int224).min, type(int224).max);
+
     try this.addExternal(
         LibDecimalFloat.pack(signedCoefficientA, exponentA), LibDecimalFloat.pack(signedCoefficientB, exponentB)
     ) returns (Float resultPacked) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function testAddUnpacked(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
external
{
try this.addExternal(
LibDecimalFloat.pack(signedCoefficientA, exponentA), LibDecimalFloat.pack(signedCoefficientB, exponentB)
) returns (Float resultPacked) {
(int256 signedCoefficient, int256 exponent) =
this.addExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
assertEq(Float.unwrap(resultPacked), Float.unwrap(LibDecimalFloat.pack(signedCoefficient, exponent)));
} catch (bytes memory err) {
vm.expectRevert(err);
this.addExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}
}
function testAddUnpacked(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
external
{
// Avoid exponent overflow errors
exponentA = bound(exponentA, EXPONENT_MIN, EXPONENT_MAX);
exponentB = bound(exponentB, EXPONENT_MIN, EXPONENT_MAX);
// Avoid coefficient overflow errors
signedCoefficientA = bound(signedCoefficientA, type(int224).min, type(int224).max);
signedCoefficientB = bound(signedCoefficientB, type(int224).min, type(int224).max);
try this.addExternal(
LibDecimalFloat.pack(signedCoefficientA, exponentA),
LibDecimalFloat.pack(signedCoefficientB, exponentB)
) returns (Float resultPacked) {
(int256 signedCoefficient, int256 exponent) =
this.addExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
assertEq(
Float.unwrap(resultPacked),
Float.unwrap(LibDecimalFloat.pack(signedCoefficient, exponent))
);
} catch (bytes memory err) {
vm.expectRevert(err);
this.addExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}
}

Comment on lines +24 to 36
function testAddPacked(Float a, Float b) external {
(int256 signedCoefficientA, int256 exponentA) = LibDecimalFloat.unpack(a);
(int256 signedCoefficientB, int256 exponentB) = LibDecimalFloat.unpack(b);
try this.addExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB) returns (
int256 signedCoefficient, int256 exponent
) {
Float memory resultMem = a.add(b);
assertEq(signedCoefficient, resultMem.signedCoefficient);
assertEq(exponent, resultMem.exponent);
Float resultMem = a.add(b);
assertEq(Float.unwrap(resultMem), Float.unwrap(LibDecimalFloat.pack(signedCoefficient, exponent)));
} catch (bytes memory err) {
vm.expectRevert(err);
a.add(b);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Fix failing test to handle coefficient overflow

The test is correctly adapting to use the packed Float type, but it's failing in CI due to coefficient overflow errors.

You need to bound the fuzzer inputs to prevent the CoefficientOverflow(1e70, -34) error. Add coefficient bounds to prevent values outside the valid range.

 function testAddPacked(Float a, Float b) external {
+    // Avoid coefficient overflow errors by bounding inputs
+    vm.assume(Float.unwrap(a) != 0 && Float.unwrap(b) != 0);
+    try LibDecimalFloat.unpack(a) returns (int256 sc, int256 e) {
+        vm.assume(sc >= type(int224).min && sc <= type(int224).max);
+    } catch {
+        return;
+    }
+    try LibDecimalFloat.unpack(b) returns (int256 sc, int256 e) {
+        vm.assume(sc >= type(int224).min && sc <= type(int224).max);
+    } catch {
+        return;
+    }
     (int256 signedCoefficientA, int256 exponentA) = LibDecimalFloat.unpack(a);
     (int256 signedCoefficientB, int256 exponentB) = LibDecimalFloat.unpack(b);
     try this.addExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB) returns (

🏁 Script executed:

#!/bin/bash
# Check the boundaries of possible inputs and the implementation of pack
ast-grep --pattern $'function pack(int256 signedCoefficient, int256 exponent) internal pure returns (Float)'

Length of output: 109


🏁 Script executed:

#!/bin/bash
# Locate the pack and unpack function definitions in the repository
rg "function pack" -n .
rg "function unpack" -n .

Length of output: 593


🏁 Script executed:

#!/bin/bash
# Display the `pack` implementation and related constants
sed -n '280,360p' src/lib/LibDecimalFloat.sol

# Search for coefficient/exponent bounds definitions
rg -n "constant" src/lib/LibDecimalFloat.sol | grep -E "COEFFICIENT|EXPONENT"

Length of output: 4748


🏁 Script executed:

#!/bin/bash
# Inspect the add test file to see external wrappers and current setup
sed -n '1,200p' test/src/lib/LibDecimalFloat.add.t.sol

Length of output: 8437


🏁 Script executed:

#!/bin/bash
# Locate and display the internal add implementation and normalize function
rg -n "function add" -n src/lib/LibDecimalFloat.sol

# Show the internal add and normalize implementations
sed -n '360,550p' src/lib/LibDecimalFloat.sol

Length of output: 9673


Update testAddPacked to correctly handle pack overflows

The current test catches only errors from the unpacked‐add path, but the CoefficientOverflow actually arises in the pack call inside a.add(b). Simply bounding the inputs to int224 isn’t sufficient to prevent an overflow on the resulting coefficient. Instead, pull the raw result from addExternal, then:

  • If it fits the int224/int32 pack bounds, assert that a.add(b) matches pack(scRes, eRes).
  • Otherwise, explicitly vm.expectRevert when calling a.add(b).

Pinpointed change in test/src/lib/LibDecimalFloat.add.t.sol:

 function testAddPacked(Float a, Float b) external {
-    (int256 signedCoefficientA, int256 exponentA) = LibDecimalFloat.unpack(a);
-    (int256 signedCoefficientB, int256 exponentB) = LibDecimalFloat.unpack(b);
-    try this.addExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB) returns (
-        int256 signedCoefficient, int256 exponent
-    ) {
-        Float resultMem = a.add(b);
-        assertEq(Float.unwrap(resultMem), Float.unwrap(LibDecimalFloat.pack(signedCoefficient, exponent)));
-    } catch (bytes memory err) {
-        vm.expectRevert(err);
-        a.add(b);
-    }
+    // Unpack inputs and get the raw add result
+    (int256 scA, int256 eA) = LibDecimalFloat.unpack(a);
+    (int256 scB, int256 eB) = LibDecimalFloat.unpack(b);
+    (int256 scRes, int256 eRes) = this.addExternal(scA, eA, scB, eB);
+
+    // If the result fits pack bounds, compare packed values,
+    // otherwise expect a CoefficientOverflow on pack.
+    if (int224(scRes) == scRes && int32(eRes) == eRes) {
+        Float resultMem = a.add(b);
+        assertEq(
+            Float.unwrap(resultMem),
+            Float.unwrap(LibDecimalFloat.pack(scRes, eRes))
+        );
+    } else {
+        bytes memory err = abi.encodeWithSelector(
+            LibDecimalFloat.CoefficientOverflow.selector,
+            scRes,
+            eRes
+        );
+        vm.expectRevert(err);
+        a.add(b);
+    }

This ensures the test only asserts on valid packable results and correctly catches overflow reverts otherwise.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function testAddPacked(Float a, Float b) external {
(int256 signedCoefficientA, int256 exponentA) = LibDecimalFloat.unpack(a);
(int256 signedCoefficientB, int256 exponentB) = LibDecimalFloat.unpack(b);
try this.addExternal(signedCoefficientA, exponentA, signedCoefficientB, exponentB) returns (
int256 signedCoefficient, int256 exponent
) {
Float memory resultMem = a.add(b);
assertEq(signedCoefficient, resultMem.signedCoefficient);
assertEq(exponent, resultMem.exponent);
Float resultMem = a.add(b);
assertEq(Float.unwrap(resultMem), Float.unwrap(LibDecimalFloat.pack(signedCoefficient, exponent)));
} catch (bytes memory err) {
vm.expectRevert(err);
a.add(b);
}
}
function testAddPacked(Float a, Float b) external {
// Unpack inputs and get the raw add result
(int256 scA, int256 eA) = LibDecimalFloat.unpack(a);
(int256 scB, int256 eB) = LibDecimalFloat.unpack(b);
(int256 scRes, int256 eRes) = this.addExternal(scA, eA, scB, eB);
// If the result fits pack bounds, compare packed values,
// otherwise expect a CoefficientOverflow on pack.
if (int224(scRes) == scRes && int32(eRes) == eRes) {
Float resultMem = a.add(b);
assertEq(
Float.unwrap(resultMem),
Float.unwrap(LibDecimalFloat.pack(scRes, eRes))
);
} else {
bytes memory err = abi.encodeWithSelector(
LibDecimalFloat.CoefficientOverflow.selector,
scRes,
eRes
);
vm.expectRevert(err);
a.add(b);
}
}

Comment thread test/src/lib/LibDecimalFloat.max.t.sol
Comment thread test/src/lib/LibDecimalFloat.min.t.sol Outdated
Comment on lines 53 to 62
function testMinXYEqual(int256 signedCoefficientX, int256 exponentX) external pure {
int256 signedCoefficientY = signedCoefficientX;
int256 exponentY = exponentX;
(int256 signedCoefficientZ, int256 exponentZ) =
LibDecimalFloat.min(signedCoefficientX, exponentX, signedCoefficientY, exponentY);
assertEq(signedCoefficientZ, signedCoefficientX, "signedCoefficientZ != signedCoefficientX");
assertEq(exponentZ, exponentX, "exponentZ != exponentX");
assertEq(signedCoefficientZ, signedCoefficientY, "signedCoefficientZ != signedCoefficientY");
assertEq(exponentZ, exponentY, "exponentZ != exponentY");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Component‑level assertions are correct, but you lost the structural check

The previous version compared packed Float values directly.
Now that you unpack, you could still assert that repacking yields the same original value to ensure the round‑trip invariance:

 assertEq(exponentZ, exponentY, "exponentZ != exponentY");
+assertEq(
+    LibDecimalFloat.pack(signedCoefficientZ, exponentZ),
+    LibDecimalFloat.pack(signedCoefficientX, exponentX),
+    "packed result != packed input"
+);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function testMinXYEqual(int256 signedCoefficientX, int256 exponentX) external pure {
int256 signedCoefficientY = signedCoefficientX;
int256 exponentY = exponentX;
(int256 signedCoefficientZ, int256 exponentZ) =
LibDecimalFloat.min(signedCoefficientX, exponentX, signedCoefficientY, exponentY);
assertEq(signedCoefficientZ, signedCoefficientX, "signedCoefficientZ != signedCoefficientX");
assertEq(exponentZ, exponentX, "exponentZ != exponentX");
assertEq(signedCoefficientZ, signedCoefficientY, "signedCoefficientZ != signedCoefficientY");
assertEq(exponentZ, exponentY, "exponentZ != exponentY");
}
function testMinXYEqual(int256 signedCoefficientX, int256 exponentX) external pure {
int256 signedCoefficientY = signedCoefficientX;
int256 exponentY = exponentX;
(int256 signedCoefficientZ, int256 exponentZ) =
LibDecimalFloat.min(signedCoefficientX, exponentX, signedCoefficientY, exponentY);
assertEq(signedCoefficientZ, signedCoefficientX, "signedCoefficientZ != signedCoefficientX");
assertEq(exponentZ, exponentX, "exponentZ != exponentX");
assertEq(signedCoefficientZ, signedCoefficientY, "signedCoefficientZ != signedCoefficientY");
assertEq(exponentZ, exponentY, "exponentZ != exponentY");
+ assertEq(
+ LibDecimalFloat.pack(signedCoefficientZ, exponentZ),
+ LibDecimalFloat.pack(signedCoefficientX, exponentX),
+ "packed result != packed input"
+ );
}

Comment on lines +329 to 334
function unpack(Float float) internal pure returns (int256 signedCoefficient, int256 exponent) {
uint256 mask = type(uint224).max;
assembly ("memory-safe") {
signedCoefficient := signextend(27, and(packed, mask))
exponent := sar(0xe0, packed)
signedCoefficient := signextend(27, and(float, mask))
exponent := sar(0xe0, float)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

signextend(27, …) is correct but deserves a comment

A quick note that byte 27 corresponds to bit 223 (the sign bit of the 224‑bit coefficient) will save future maintainers from recalculating the magic number.

// 27 = byte index of bit 223 (sign bit for 224‑bit two’s‑complement)
signedCoefficient := signextend(27, and(float, COEFF_MASK));

Comment on lines 101 to +103
library LibDecimalFloat {
using LibDecimalFloat for Float;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

using LibDecimalFloat for Float; inside the same library is redundant

Because the functions are defined in this library, callers only need to write

using LibDecimalFloat for Float;

where they consume the library.
Keeping it here does nothing inside the library itself and slightly increases byte‑code size.

Comment thread src/lib/LibDecimalFloat.sol Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

🔭 Outside diff range comments (3)
test/src/lib/implementation/LibDecimalFloatImplementation.inv.t.sol (1)

1-37: 🧹 Nitpick (assertive)

Test coverage for inv function looks good but could be expanded

The implementation properly tests the inv function against a reference implementation and includes gas benchmarks. The fuzzing with appropriate bounds ensures comprehensive testing.

Consider adding a few specific test cases with predefined inputs and expected outputs to make the test more robust and to provide clearer documentation of expected behavior for common cases.

+ function testSpecificInvCases() external {
+     // Test case 1: Inverse of 2
+     (int256 outputSignedCoefficient, int256 outputExponent) = LibDecimalFloatImplementation.inv(2, 0);
+     assertEq(outputSignedCoefficient, 5, "coefficient");
+     assertEq(outputExponent, -1, "exponent");
+     
+     // Test case 2: Inverse of 0.5
+     (outputSignedCoefficient, outputExponent) = LibDecimalFloatImplementation.inv(5, -1);
+     assertEq(outputSignedCoefficient, 2, "coefficient");
+     assertEq(outputExponent, 0, "exponent");
+     
+     // Test case 3: Inverse of -10
+     (outputSignedCoefficient, outputExponent) = LibDecimalFloatImplementation.inv(-1, 1);
+     assertEq(outputSignedCoefficient, -1, "coefficient");
+     assertEq(outputExponent, -1, "exponent");
+ }
test/src/lib/LibDecimalFloat.eq.t.sol (1)

24-34: 🧹 Nitpick (assertive)

Good defensive parity check – tiny style nit

eqExternal (the packed variant) is already pure; marking the whole test
function testEqPacked as pure as well tightens the contract and gives the
optimizer more room to work. It currently uses only pure operations.

-function testEqPacked(Float a, Float b) external {
+function testEqPacked(Float a, Float b) external pure {
src/lib/implementation/LibDecimalFloatImplementation.sol (1)

693-714: 🧹 Nitpick (assertive)

compareRescale uses wrapped‑overflow sentinel; clarify intent

The wrapped multiplication rescaled = signedCoefficientA * scale intentionally
allows overflow and detects it via rescaled / scale != signedCoefficientA.
This is clever but brittle—future Solidity versions may change wrap semantics.
A safer pattern is the checked‐mul helper from OpenZeppelin which reverts on
overflow, letting you fall back to the “infinitely larger” branch in the
catch clause.

♻️ Duplicate comments (4)
src/lib/parse/LibParseDecimalFloat.sol (1)

90-90: Reduced precision loss threshold from 77 to 67 digits

The precision loss threshold for fractional parsing has been reduced from 77 to 67 digits. This change might cause more cases to be rejected as precision loss.

#!/bin/bash
# Check for existing tests that rely on the previous threshold value
rg -A 2 -B 2 "77" --glob "test/*.sol" | grep -i "precision"
test/src/lib/format/LibFormatDecimalFloat.t.sol (1)

37-46: Fix CoefficientOverflow in testFormatDecimalRoundTrip

The function was renamed from testRoundTrip and updated to use packed Float, but the value bound may still be too large, potentially causing CoefficientOverflow errors as noted in previous reviews.

#!/bin/bash
# Check if there are any CoefficientOverflow errors occurring in tests

# Look at error definitions and implementation to understand coefficient limits
rg -A 5 "CoefficientOverflow"

# Check if test failures are occurring related to this error
rg -A 5 "testFormatDecimalRoundTrip.*fail" -g "*.json"

Consider limiting the bound to a smaller value to prevent overflow:

-        value = bound(value, 0, type(uint256).max / 10);
+        // Ensure value doesn't exceed coefficient capacity for packed float
+        value = bound(value, 0, 1e40);
src/lib/LibDecimalFloat.sol (2)

88-89: Redundant using … for inside the defining library (repeat)

This was called out in a previous review and is still present.
Declaring using LibDecimalFloat for Float; inside LibDecimalFloat has no effect and only inflates the byte‑code.


343-345: Add the explanatory comment for signextend(27, …)

Prior feedback suggested documenting why byte 27 is chosen.
This magic constant still lacks context and forces every reader to recalculate where bit 223 lives.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f370770 and 207730b.

📒 Files selected for processing (35)
  • src/lib/LibDecimalFloat.sol (13 hunks)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (4 hunks)
  • src/lib/parse/LibParseDecimalFloat.sol (3 hunks)
  • test/abstract/LogTest.sol (1 hunks)
  • test/lib/LibDecimalFloatSlow.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.abs.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.add.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.divide.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.eq.t.sol (3 hunks)
  • test/src/lib/LibDecimalFloat.floor.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.frac.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.gt.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.inv.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.log10.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.lt.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.max.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.min.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.minus.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.mixed.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.multiply.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.normalize.t.sol (0 hunks)
  • test/src/lib/LibDecimalFloat.pack.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.power.t.sol (3 hunks)
  • test/src/lib/LibDecimalFloat.power10.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.sub.t.sol (1 hunks)
  • test/src/lib/format/LibFormatDecimalFloat.t.sol (2 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.add.t.sol (1 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.divide.t.sol (1 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.eq.t.sol (1 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.inv.t.sol (1 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.log10.t.sol (1 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.minus.t.sol (1 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.multiply.t.sol (1 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.power10.t.sol (1 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.sub.t.sol (1 hunks)
💤 Files with no reviewable changes (1)
  • test/src/lib/LibDecimalFloat.normalize.t.sol
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: git-clean
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
🔇 Additional comments (91)
test/abstract/LogTest.sol (1)

4-5: Good addition of console logging facilities.

The re-export of console2 from forge-std's Test.sol enhances debugging capabilities across test contracts. This change aligns well with the broader refactoring effort, providing a convenient way for test contracts to access logging functionality without additional imports.

test/lib/LibDecimalFloatSlow.sol (2)

5-8: Import and using directive properly align with Float refactoring.

The import of the Float type and addition of the using directive are consistent with the codebase refactoring to use the packed Float type alias.


43-43: Implementation correctly updated to use core implementation library.

The invSlow function now properly calls LibDecimalFloatImplementation.divide instead of the previous implementation, consistent with the architectural changes to delegate core arithmetic operations to the implementation library.

test/src/lib/LibDecimalFloat.mixed.t.sol (3)

4-10: Proper setup for using the packed Float type.

Correctly imports the Float type and sets up the using directive to enable method-based operations on the Float type.


14-17: Test refactored correctly to use packed Float operations.

The division operation has been updated to use the packed Float representation and method-based approach, consistent with the broader refactoring effort.


21-22: Multiply operation properly updated to use packed Float.

The multiplication step correctly uses the method-based approach on packed Float values, maintaining the logical equivalence with the previous implementation while aligning with the new architecture.

test/src/lib/implementation/LibDecimalFloatImplementation.minus.t.sol (1)

1-26: Well-structured test for the minus operation.

This new test contract effectively verifies that the minus operation (negation) produces the same results as subtracting the value from zero. The test bounds input exponents appropriately to prevent overflow conditions and includes clear assertions to validate both the coefficient and exponent outputs.

The test complements the other implementation tests and helps ensure the correctness of the core arithmetic operations in the refactored codebase.

test/src/lib/implementation/LibDecimalFloatImplementation.log10.t.sol (1)

1-53: Well-structured test implementation for log10 functionality

This is a good implementation of tests for the log10 function. The test coverage is comprehensive, testing exact powers of 10, exact lookup values, interpolated lookups, and values less than 1. The helper function checkLog10 effectively centralizes the verification logic and includes gas measurement for performance analysis.

src/lib/parse/LibParseDecimalFloat.sol (3)

16-16: Updated import to use the packed Float type

The import has been updated to use the packed Float type alias from LibDecimalFloat.sol, which aligns with the broader refactoring of decimal float handling in the codebase.


20-28: Refactored to use the packed Float type

The function signature has been updated to return the packed Float type instead of PackedFloat, and the error handling and return path now use the packed type. This aligns with the broader refactoring of decimal float handling in the codebase.


128-139: Simplified function signature to use packed Float type

The function signature has been simplified by removing named return variables and now returns a tuple (bytes4, Float) instead of (bytes4, Float memory). It also directly returns the packed float from the parsed coefficient and exponent.

test/src/lib/LibDecimalFloat.minus.t.sol (4)

4-5: Updated import and simplified dependency

The import has been updated to use the packed Float type alias from LibDecimalFloat.sol and removed unnecessary constants. This aligns with the broader refactoring of decimal float handling in the codebase.


12-14: Redirected external function to implementation library

The minusExternal function now delegates to LibDecimalFloatImplementation.minus instead of LibDecimalFloat.minus, consistent with the architectural changes where core arithmetic operations are moved to the implementation library.


16-18: Updated function signature to use packed Float type

The function signature has been updated to take and return Float by value rather than memory references, reflecting the new packed type usage.


20-32: Refactored test to handle packed Float values

The test function has been renamed from testMinusMem to testMinusPacked and updated to work with packed Float values. It explicitly unpacks and repacks values for comparison.

test/src/lib/implementation/LibDecimalFloatImplementation.sub.t.sol (1)

11-49: Well-structured tests for the subtraction implementation

The implementation tests for the subtraction operation are thorough and well-designed:

  1. The main test verifies that subtraction is equivalent to addition with a negated second coefficient
  2. A separate test handles the edge case with the minimum signed value
  3. Bounds are properly set for exponents to prevent overflow issues
  4. The test coverage is comprehensive with proper documentation
test/src/lib/format/LibFormatDecimalFloat.t.sol (2)

20-21: Changed signature to use packed Float type instead of memory struct

The function now correctly accepts a packed Float value instead of a memory struct, aligning with the consolidation to packed Float types.


25-33: Updated to unpack Float before comparison

The test now appropriately unpacks the Float value to get its components before calling the external function, which is consistent with the new packed Float representation approach.

test/src/lib/LibDecimalFloat.pack.t.sol (4)

4-4: Updated imports to include Float type and error definitions

The import now correctly includes the Float type, ExponentOverflow error, and EXPONENT_MAX constant from LibDecimalFloat.


9-11: Updated packing function to use new packLossy API

The function signature has been improved to return a tuple with the packed Float value and a boolean indicating if the packing was lossless, which provides better error handling.


13-15: Updated unpacking function to use new Float type

The function signature has been correctly updated to accept a packed Float value instead of the previous type, aligning with the consolidation approach.


19-22: Updated round-trip test to use the new packed Float type

The test now correctly:

  1. Uses packLossy instead of the previous packing method
  2. Captures the lossless flag and asserts it's true
  3. Unpacks the Float to verify coefficient and exponent values

These changes properly validate the round-trip functionality with the new API.

test/src/lib/LibDecimalFloat.frac.t.sol (3)

11-13: Simplified test to use Float type directly

The test has been simplified to directly use the Float type and its frac() method instead of external function calls with separate parameters.


16-19: Updated helper function to use packed Float representation

The checkFrac helper function now:

  1. Creates a packed Float using packLossless
  2. Calls the frac() method on the Float directly
  3. Unpacks the result to verify against expected values

This is consistent with the overall refactoring to use packed Float values throughout the codebase.


73-74: Updated gas test functions to use packed Float

All gas benchmark functions have been correctly updated to use the new packed Float representation with packLossless and the frac() method.

Also applies to: 78-79, 83-84

test/src/lib/LibDecimalFloat.divide.t.sol (4)

4-5: Import changes align with the consolidated Float approach.

The imports have been updated to include both the refactored LibDecimalFloat with the packed Float type and the new implementation library. This matches the architectural shift toward a packed representation.


17-17: Delegation to implementation library is properly implemented.

The division logic is now delegated to LibDecimalFloatImplementation, which is consistent with the new architecture that separates interface from implementation.


20-20: Function signature updated to use packed Float by value.

The external division function now accepts Float parameters by value instead of memory reference, aligning with the packed Float type alias approach.


24-37: Test function properly updated to use packed representation.

The test has been renamed from testDivideMem to testDividePacked and now:

  1. Correctly unpacks the input Floats using .unpack()
  2. Tests both the primitive and packed implementations
  3. Properly compares results using unpacked components
  4. Maintains equivalent error handling

This ensures that both implementation approaches produce consistent results.

test/src/lib/implementation/LibDecimalFloatImplementation.add.t.sol (7)

1-29: Well-structured basic addition tests.

The test contract starts with clear, well-commented test cases for fundamental addition scenarios including zero addition, normalization handling, and boundary cases. The tests use descriptive names and include clear expected outcomes in comments.


46-58: Comprehensive normalization tests.

These tests verify that addition correctly handles normalization with both non-normalized and pre-normalized inputs, ensuring the implementation maintains numerical precision regardless of input format.


76-82: Gas efficiency tests included.

Good practice to include gas consumption tests for common operations like adding zero and adding one. This will help identify any performance regressions in future changes.


84-97: Robust fuzz testing approach.

The test properly bounds exponent inputs to prevent overflow/underflow and uses fuzzing to verify the addition implementation never reverts within valid input ranges. This is important for ensuring stability of the library.


99-124: Thorough testing of precision behavior.

This fuzz test verifies an important property: when adding a small number to a large one (beyond a certain exponent difference threshold), the result should equal the larger number. This validates proper handling of precision limitations.


126-178: Comprehensive examples with expected precision loss.

These examples clearly demonstrate and verify the expected behavior for precision loss cases, validating the implementation handles edge cases correctly. The tests cover both positive and negative numbers and different exponent combinations.


180-221: Property-based testing for addition edge cases.

These tests verify two important properties:

  1. Adding numbers with the same exponent correctly adds their coefficients
  2. Adding zero to any non-zero number returns the non-zero number unchanged

Both properties are tested with thorough input variations and edge cases.

test/src/lib/implementation/LibDecimalFloatImplementation.power10.t.sol (4)

8-20: Well-structured helper for power10 testing.

The checkPower10 helper function improves test readability and maintenance by centralizing the assertion logic, retrieving the log tables, and providing descriptive error messages.


22-30: Tests for basic power10 calculations.

These tests verify the core functionality for basic integer exponents, ensuring the implementation returns correctly normalized results.


32-57: Comprehensive tests for exact lookup values.

The tests verify both integer and fractional powers of 10, covering various lookup table entries. The inclusion of negative inputs (line 56) is particularly valuable for edge case validation.


59-64: Tests for interpolated values verify approximation quality.

These tests ensure the library correctly handles fractional powers that require interpolation between lookup table entries, including a test with a very large exponent (line 63).

test/src/lib/implementation/LibDecimalFloatImplementation.multiply.t.sol (4)

14-53: Thorough testing of basic multiplication cases.

The tests cover all fundamental cases (zero multiplication, multiplying by one) with various exponent combinations. Proper validation against the normalized zero constants ensures consistent behavior.


55-80: Comprehensive tests for number multiplication.

Tests verify exact multiplication results for specific inputs (like 123456789 * 987654321) both with and without exponents, as well as power-of-ten calculations with positive and negative exponents.


82-90: Gas consumption tests included.

Including gas tests for common operations (multiplying by zero and one) is good practice for identifying potential performance regressions.


92-107: Strong property-based validation against reference implementation.

This comprehensive fuzz test:

  1. Bounds inputs to valid exponent ranges
  2. Compares results against a reference implementation (LibDecimalFloatSlow.multiplySlow)
  3. Verifies both coefficient and exponent correctness

This approach provides high confidence in the implementation's correctness across a wide range of inputs.

test/src/lib/LibDecimalFloat.multiply.t.sol (4)

4-4: Good refactoring of imports.

Simplified the import to only include essential constants and types from LibDecimalFloat, making dependencies clearer.


17-17: Implementation delegation looks good.

Updated to call LibDecimalFloatImplementation.multiply instead of direct functionality, aligning with the codebase's refactoring toward centralized implementation.


20-20: Good signature refactoring.

Function now takes Float parameters by value rather than memory references, consistent with the shift to packed representation.


24-34: Test function properly updated for packed representation.

Renamed from testMultiplyMem to testMultiplyPacked and correctly updated to unpack Float inputs before calling the external function, then compare unpacked results.

test/src/lib/implementation/LibDecimalFloatImplementation.eq.t.sol (5)

1-7: Well-organized imports and license declaration.

Clean organization with proper license declaration and focused imports.


9-27: Comprehensive gas benchmark tests.

Good coverage of edge cases including different signs, zero values, and exponent overflow scenarios.


30-52: Thorough property-based equality tests.

The test validates the mathematical relationship between equal decimal floats with different representations.


55-93: Excellent coverage of equality edge cases.

Tests handle different values, same values with different exponents, zero values, and reflexivity properties.


95-103: Good reference test implementation.

Comparing against a reference implementation ensures correctness of the equality function.

test/src/lib/LibDecimalFloat.floor.t.sol (3)

11-12: Test properly updated for packed representation.

Updated to use the packed Float type for testing, consistent with the codebase refactoring approach.


16-17: Helper function correctly adjusted.

The checkFloor function now packs inputs into Float structs before applying floor operations, maintaining test integrity while using the new representation.


79-80: Gas benchmark tests properly updated.

Gas benchmark tests have been updated to use the new Float struct representation consistently.

Also applies to: 84-85, 89-90

test/src/lib/LibDecimalFloat.inv.t.sol (3)

5-6: Appropriate import updates.

Imports updated to reference the correct modules and types for the refactored implementation.


12-12: Implementation delegation looks good.

Updated to call LibDecimalFloatImplementation.inv, aligning with the centralized implementation pattern.


15-15: Good signature refactoring.

Function now takes the Float parameter by value rather than a memory reference, consistent with the packed representation approach.

test/src/lib/LibDecimalFloat.log10.t.sol (4)

5-5: Added dependency on LibDecimalFloatImplementation.

This implements the architectural change to delegate core operations to the implementation library.


13-13: Delegated log10 to LibDecimalFloatImplementation.

The function now calls the implementation library version of log10 instead of the original library.


16-18: Updated signature to use packed Float by value.

The function now takes and returns Float by value (stack) rather than memory reference, aligning with the broader shift to the packed representation.


20-29: Refactored test to use packed Float values.

The renamed testLog10Packed function properly:

  1. Unpacks the Float type into components
  2. Tests the implementation function with unpacked components
  3. Tests the Float interface directly
  4. Compares the unpacked components for equality

This provides good test coverage for the equivalence between the packed and unpacked interfaces.

test/src/lib/LibDecimalFloat.min.t.sol (4)

11-14: Simplified function signature to use packed Float by value.

Changed function to accept and operate on the packed Float type directly rather than a memory reference, aligning with the architectural shift away from memory-based structs.


17-21: Updated function parameters to use packed Float.

Changed function to accept packed Float values and removed memory references for local variables, aligning with the architectural change.


24-29: Simplified function to use packed Float values.

Changed test to operate on packed values directly, while maintaining the same test logic and assertions.


32-37: Updated function to use packed Float values while maintaining test logic.

Changed to use the packed Float type directly, keeping the core test logic intact.

test/src/lib/LibDecimalFloat.max.t.sol (4)

11-14: Updated function to use packed Float type.

Function now receives and handles Float as a value type rather than a memory reference.


17-21: Refactored to use packed Float values.

Updated the function to use the new packed Float representation, maintaining the same test assertions.


24-29: Updated function parameters and variables to use packed Float.

Simplified to use the packed representation directly, keeping the same test logic.


40-45: Updated to use packed Float values.

Function now uses the packed Float type directly, maintaining the same test semantics.

test/src/lib/LibDecimalFloat.sub.t.sol (3)

17-17: Delegated subtraction logic to LibDecimalFloatImplementation.

Changed to call the implementation library directly for the core logic, aligning with the architectural shift.


20-22: Updated function signature to use packed Float by value.

Changed to take packed Float values and return a packed Float, removing memory references to align with the architectural change.


24-34: Refactored test to use packed Float values.

The test now:

  1. Takes packed Float parameters
  2. Unpacks them for the primitive component test
  3. Calls both APIs (implementation and Float interface)
  4. Verifies component-level equivalence

This approach properly tests the compatibility between the packed interface and primitive component implementation.

test/src/lib/LibDecimalFloat.abs.t.sol (3)

12-19: LGTM: Non-negative absolute value test looks correct.

The test properly uses the new packed Float approach, creating and validating values with appropriate bounds. The coefficient is bounded to type(int224).max which matches the expected internal storage format.


22-29: LGTM: Negative value handling looks good.

The test correctly bounds the coefficient between type(int224).min + 1 and -1, which avoids the special case of the minimum int224 value that's tested separately. It then properly validates that the absolute value negates the coefficient while preserving the exponent.


32-39: LGTM: Special case handling for minimum value is correct.

The test handles the special edge case where negating type(int224).min would overflow int224. The implementation correctly shifts the result one order of magnitude (dividing by 10 and incrementing the exponent).

test/src/lib/implementation/LibDecimalFloatImplementation.divide.t.sol (5)

12-25: Well-structured test helper for division validation.

The checkDivision helper function properly verifies both the coefficient and exponent parts of division results, making the test cases more readable and maintainable.


27-39: LGTM: Gas benchmark for repeated division operations.

This test provides valuable gas usage insights by executing the same division operation multiple times in sequence.


41-75: LGTM: Good coverage of basic division cases.

These tests cover important division scenarios including:

  • Normal division (1/3)
  • Negative division (-1/3)
  • Large decimals (1e18/3)
  • Various magnitude combinations

77-94: LGTM: Comprehensive compound division test.

The test validates that (1/9) divided by (1/3) correctly equals 1/3, demonstrating that division works correctly with results of previous divisions.


96-125: Excellent fuzz test for division with varying scales.

This fuzz test systematically tests division across different scales while ensuring precision is maintained. The nested loops verify that all combinations of scales produce normalized results with the expected precision.

test/src/lib/LibDecimalFloat.power.t.sol (3)

7-7: Added implementation import looks good.

Adding the import for LibDecimalFloatImplementation aligns with the new architecture where core operations are implemented in this separate library.


14-32: Well-refactored power test function.

The checkPower function has been properly updated to:

  1. Create packed Float instances using packLossless
  2. Call power through the Float method with log tables
  3. Unpack the result for assertion against expected values
  4. Maintain gas measurements for performance tracking

This aligns well with the new architecture.


39-50: Properly updated round-trip test.

The checkRoundTrip function has been correctly refactored to use the packed Float approach, using packLossless for inputs and the appropriate Float methods for power, inverse, division, subtraction, and comparison operations.

test/src/lib/LibDecimalFloat.add.t.sol (4)

4-8: Good import structure with specific constants.

The import has been improved to specifically include the ADD_MAX_EXPONENT_DIFF constant from the implementation library, which provides better context for the allowed exponent difference in addition operations.


15-21: LGTM: Updated external test function delegates to implementation.

The addExternal function now correctly delegates to LibDecimalFloatImplementation.add for the unpacked parameters, aligning with the architecture update.


23-25: LGTM: Simplified external function signature.

The packed addExternal function now returns a Float directly instead of a tuple, which simplifies the interface and better reflects the updated library architecture.


27-41:

Details

❌ Incorrect review comment

Handle overflow conditions in the test.

The test does not explicitly handle potential coefficient or exponent overflow conditions. According to past review comments, this test was failing due to overflow issues.

Consider adding explicit bounds checks for inputs:

  1. Bound coefficients to type(int224).min/max
  2. Bound exponents appropriately
  3. Handle packing overflow cases explicitly

🏁 Script executed:

#!/bin/bash
# Find instances of overflow handling in similar test files
rg -A 5 "overflow|bound\(" --glob "*.t.sol" | grep -A 2 "bound"

Length of output: 19080


Overflow handling already covered by try/catch in testAddPacked

The testAddPacked function explicitly wraps the low‑level and high‑level addExternal calls in a try { … } catch (bytes memory err) { vm.expectRevert(err); … } block, so both successful additions and overflow‐induced reverts are already being tested. No additional bound(…) constraints are needed here.

Likely an incorrect or invalid review comment.

test/src/lib/LibDecimalFloat.eq.t.sol (1)

37-41: Excellent cross‑relation assertion

Linking eq, lt, gt the way you did guards against subtle ordering bugs.

src/lib/LibDecimalFloat.sol (1)

291-309: 🧹 Nitpick (assertive)

while (int224(signedCoefficient) != signedCoefficient) may hot‑loop on huge inputs

signedCoefficient can be a 256‑bit value supplied by users of packLossy.
If it is close to 2²⁵⁵ the while loop will execute up to 32 iterations (one per dropped decimal), which is fine.
But for pathological values (e.g. 10**255) the loop will iterate ~ 255 times and burn > 60 k gas.

Consider short‑circuiting with a logarithmic approach:

uint256 digits = _numDigits(signedCoefficient);
if (digits > 224) {
    uint256 trim = digits - 224;
    uint256 step = trim > 1 ? trim - 1 : 1;
    signedCoefficient /= 10 ** step;
    exponent      += int256(step);
}

That reduces the worst‑case iterations to two.

Likely an incorrect or invalid review comment.

Comment thread test/src/lib/implementation/LibDecimalFloatImplementation.power10.t.sol Outdated
Comment thread test/src/lib/implementation/LibDecimalFloatImplementation.eq.t.sol
Comment on lines +20 to 29
function testInvMem(Float float) external {
(int256 signedCoefficient, int256 exponent) = float.unpack();
try this.invExternal(signedCoefficient, exponent) returns (
int256 signedCoefficientResult, int256 exponentResult
) {
Float memory floatInv = this.invExternal(float);
assertEq(signedCoefficient, floatInv.signedCoefficient);
assertEq(exponent, floatInv.exponent);
Float floatInv = this.invExternal(float);
(int256 signedCoefficientResultUnpacked, int256 exponentResultUnpacked) = floatInv.unpack();
assertEq(signedCoefficientResultUnpacked, signedCoefficientResult);
assertEq(exponentResultUnpacked, exponentResult);
} catch (bytes memory err) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Test function needs name update.

While the test logic has been correctly updated to work with packed Float values, the function name "testInvMem" suggests it's testing memory-based implementation.

Rename the function to better reflect its purpose:

-function testInvMem(Float float) external {
+function testInvPacked(Float float) external {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function testInvMem(Float float) external {
(int256 signedCoefficient, int256 exponent) = float.unpack();
try this.invExternal(signedCoefficient, exponent) returns (
int256 signedCoefficientResult, int256 exponentResult
) {
Float memory floatInv = this.invExternal(float);
assertEq(signedCoefficient, floatInv.signedCoefficient);
assertEq(exponent, floatInv.exponent);
Float floatInv = this.invExternal(float);
(int256 signedCoefficientResultUnpacked, int256 exponentResultUnpacked) = floatInv.unpack();
assertEq(signedCoefficientResultUnpacked, signedCoefficientResult);
assertEq(exponentResultUnpacked, exponentResult);
} catch (bytes memory err) {
function testInvPacked(Float float) external {
(int256 signedCoefficient, int256 exponent) = float.unpack();
try this.invExternal(signedCoefficient, exponent) returns (
int256 signedCoefficientResult, int256 exponentResult
) {
Float floatInv = this.invExternal(float);
(int256 signedCoefficientResultUnpacked, int256 exponentResultUnpacked) = floatInv.unpack();
assertEq(signedCoefficientResultUnpacked, signedCoefficientResult);
assertEq(exponentResultUnpacked, exponentResult);
} catch (bytes memory err) {

Comment thread test/src/lib/LibDecimalFloat.min.t.sol
Comment thread test/src/lib/LibDecimalFloat.max.t.sol
Comment on lines +198 to +210
internal
pure
returns (int256, int256)
{
unchecked {
(signedCoefficientA, exponentA) = LibDecimalFloatImplementation.normalize(signedCoefficientA, exponentA);
(signedCoefficientB, exponentB) = LibDecimalFloatImplementation.normalize(signedCoefficientB, exponentB);

int256 signedCoefficient = (signedCoefficientA * 1e38) / signedCoefficientB;
int256 exponent = exponentA - exponentB - 38;
return (signedCoefficient, exponent);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Missing explicit divisor‑zero check

divide relies on EVM’s / to revert on zero‑divisor. Explicitly reverting
with a domain‑specific error (e.g. DivisionByZero()), as you did elsewhere
(Log10Zero), makes the API clearer and prevents silent 0 results if a
future refactor accidentally uses unchecked {} division.

- int256 signedCoefficient = (signedCoefficientA * 1e38) / signedCoefficientB;
+ if (signedCoefficientB == 0) revert DivisionByZero();
+ int256 signedCoefficient = (signedCoefficientA * 1e38) / signedCoefficientB;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
internal
pure
returns (int256, int256)
{
unchecked {
(signedCoefficientA, exponentA) = LibDecimalFloatImplementation.normalize(signedCoefficientA, exponentA);
(signedCoefficientB, exponentB) = LibDecimalFloatImplementation.normalize(signedCoefficientB, exponentB);
int256 signedCoefficient = (signedCoefficientA * 1e38) / signedCoefficientB;
int256 exponent = exponentA - exponentB - 38;
return (signedCoefficient, exponent);
}
}
internal
pure
returns (int256, int256)
{
unchecked {
(signedCoefficientA, exponentA) = LibDecimalFloatImplementation.normalize(signedCoefficientA, exponentA);
(signedCoefficientB, exponentB) = LibDecimalFloatImplementation.normalize(signedCoefficientB, exponentB);
if (signedCoefficientB == 0) revert DivisionByZero();
int256 signedCoefficient = (signedCoefficientA * 1e38) / signedCoefficientB;
int256 exponent = exponentA - exponentB - 38;
return (signedCoefficient, exponent);
}
}

Comment thread test/src/lib/LibDecimalFloat.lt.t.sol Outdated
Comment thread test/src/lib/LibDecimalFloat.gt.t.sol Outdated
Comment on lines 13 to 20
function testGtReference(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
external
pure
{
bool actual = LibDecimalFloat.gt(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
Float a = LibDecimalFloat.packLossless(signedCoefficientA, exponentA);
Float b = LibDecimalFloat.packLossless(signedCoefficientB, exponentB);
bool actual = a.gt(b);
bool expected = LibDecimalFloatSlow.gtSlow(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Same un‑bounded exponents issue here

testGtReference also builds packed floats straight from un‑bounded int256 exponents and will revert for out‑of‑range values. Add the same bound() guard as suggested for the lt suite.

Comment on lines +355 to 364
function add(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.add(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
(Float c, bool lossless) = packLossy(signedCoefficient, exponent);
// Addition can be lossy.
(lossless);
return c;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Unused lossless variables – replace with _ to silence the compiler

Each arithmetic wrapper stores the second return value of packLossy in lossless and then discards it with (lossless);.
Using an underscore makes the intent clearer and avoids future lint warnings:

- (Float c, bool lossless) = packLossy(signedCoefficient, exponent);
- // Addition can be lossy.
- (lossless);
+ (Float c, ) = packLossy(signedCoefficient, exponent); // addition can be lossy

Apply to sub, multiply, divide, minus, abs, frac, floor, power10, log10 and power.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function add(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.add(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
(Float c, bool lossless) = packLossy(signedCoefficient, exponent);
// Addition can be lossy.
(lossless);
return c;
}
function add(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.add(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
(Float c, ) = packLossy(signedCoefficient, exponent); // addition can be lossy
return c;
}

Comment on lines +324 to 330
function packLossless(int256 signedCoefficient, int256 exponent) internal pure returns (Float) {
(Float c, bool lossless) = packLossy(signedCoefficient, exponent);
if (!lossless) {
revert CoefficientOverflow(signedCoefficient, exponent);
}
return c;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

packLossless re‑packs even when the lossy path already reverted

packLossy will already revert on an exponent overflow, so the additional check here is only guarding the coefficient width. To avoid the double‑work (and gas) you can inline the two checks:

if (int224(signedCoefficient) != signedCoefficient) {
    revert CoefficientOverflow(signedCoefficient, exponent);
}
if (int32(exponent) != exponent) {
    revert ExponentOverflow(signedCoefficient, exponent);
}

This also documents exactly which invariant failed.

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (4)
test/src/lib/implementation/LibDecimalFloatImplementation.power10.t.sol (1)

77-81: 🛠️ Refactor suggestion

Fuzz test only checks “no‑revert”; validate at least basic invariants
(This is the same remark made on earlier commits.)
Relying solely on testNoRevert leaves a vast surface for silent numeric errors. Consider asserting inv(power10(x)) ≈ 10^x for random inputs or comparing against a high‑precision reference in Foundry’s vm.remember/vm.recall.

src/lib/implementation/LibDecimalFloatImplementation.sol (3)

16-17: 🧹 Nitpick (assertive)

Magic number 37 still undocumented

Past reviews asked for an inline explanation of why ADD_MAX_EXPONENT_DIFF is precisely 37.
A one‑line comment (e.g. “// 37 keeps signedCoefficientA * 10^diff ≤ 1e75 < 2²⁵⁵”) would prevent future maintainers from “fixing” the number and silently re‑introducing overflow.


314-323: 🧹 Nitpick (assertive)

Gas: computing 10 ** alignmentExponentDiff in every call is expensive

alignmentExponentDiff is restricted to [0,37], so a small constant lookup table (or even the POW10 array suggested in earlier reviews) avoids the dynamic exponentiation and saves ~200 gas on each addition where exponents differ.

uint256 constant POW10[38] = [1, 10, 100, … 1e37];
multiplier = POW10[alignmentExponentDiff];

845-860: 🧹 Nitpick (assertive)

Multiplication inside unitLinearInterpolation can still overflow
Previous feedback highlighted that multiply(xDiff, yDiff) can hit int256 limits before normalisation rescales. The new code calls multiply directly but doesn’t bound either operand; a defensive normalize (or the overflow‑aware multiply logic reused here) would make the path provably safe.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 207730b and 0094e7d.

📒 Files selected for processing (2)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (4 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.power10.t.sol (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: git-clean
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)

Comment on lines +109 to +145
function multiply(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
internal
pure
returns (int256, int256)
{
unchecked {
// Unchecked mul the coefficients and add the exponents.
int256 signedCoefficient = signedCoefficientA * signedCoefficientB;

// Need to return early if the result is zero to avoid divide by
// zero in the overflow check.
if (signedCoefficient == 0) {
return (NORMALIZED_ZERO_SIGNED_COEFFICIENT, NORMALIZED_ZERO_EXPONENT);
}

int256 exponent = exponentA + exponentB;

// No jumps to see if we overflowed.
bool didOverflow;
assembly ("memory-safe") {
didOverflow :=
or(
iszero(eq(sdiv(signedCoefficient, signedCoefficientA), signedCoefficientB)),
iszero(eq(sub(exponent, exponentA), exponentB))
)
}
// If we did overflow, normalize and try again. Normalized values
// cannot overflow, so this will always succeed, provided the
// exponents are not out of bounds.
if (didOverflow) {
(signedCoefficientA, exponentA) = normalize(signedCoefficientA, exponentA);
(signedCoefficientB, exponentB) = normalize(signedCoefficientB, exponentB);
return multiply(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}
return (signedCoefficient, exponent);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

multiply can recurse unnecessarily; pre‑normalize instead

When didOverflow is true you normalise both operands and then call multiply again, causing an extra stack frame and doubling gas for the happy path.
Normalising before the first multiplication avoids the recursive call altogether:

-        unchecked {
-            int256 signedCoefficient = signedCoefficientA * signedCoefficientB;
+        unchecked {
+            (signedCoefficientA, exponentA) = normalize(signedCoefficientA, exponentA);
+            (signedCoefficientB, exponentB) = normalize(signedCoefficientB, exponentB);
+            int256 signedCoefficient = signedCoefficientA * signedCoefficientB;

This keeps the overflow check but guarantees a single execution path.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function multiply(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
internal
pure
returns (int256, int256)
{
unchecked {
// Unchecked mul the coefficients and add the exponents.
int256 signedCoefficient = signedCoefficientA * signedCoefficientB;
// Need to return early if the result is zero to avoid divide by
// zero in the overflow check.
if (signedCoefficient == 0) {
return (NORMALIZED_ZERO_SIGNED_COEFFICIENT, NORMALIZED_ZERO_EXPONENT);
}
int256 exponent = exponentA + exponentB;
// No jumps to see if we overflowed.
bool didOverflow;
assembly ("memory-safe") {
didOverflow :=
or(
iszero(eq(sdiv(signedCoefficient, signedCoefficientA), signedCoefficientB)),
iszero(eq(sub(exponent, exponentA), exponentB))
)
}
// If we did overflow, normalize and try again. Normalized values
// cannot overflow, so this will always succeed, provided the
// exponents are not out of bounds.
if (didOverflow) {
(signedCoefficientA, exponentA) = normalize(signedCoefficientA, exponentA);
(signedCoefficientB, exponentB) = normalize(signedCoefficientB, exponentB);
return multiply(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}
return (signedCoefficient, exponent);
}
}
function multiply(int256 signedCoefficientA, int256 exponentA, int256 signedCoefficientB, int256 exponentB)
internal
pure
returns (int256, int256)
{
unchecked {
// Pre‑normalize inputs to avoid the recursive overflow‑retry path.
(signedCoefficientA, exponentA) = normalize(signedCoefficientA, exponentA);
(signedCoefficientB, exponentB) = normalize(signedCoefficientB, exponentB);
// Unchecked mul the coefficients and add the exponents.
int256 signedCoefficient = signedCoefficientA * signedCoefficientB;
// Need to return early if the result is zero to avoid divide by
// zero in the overflow check.
if (signedCoefficient == 0) {
return (NORMALIZED_ZERO_SIGNED_COEFFICIENT, NORMALIZED_ZERO_EXPONENT);
}
int256 exponent = exponentA + exponentB;
// No jumps to see if we overflowed.
bool didOverflow;
assembly ("memory-safe") {
didOverflow :=
or(
iszero(eq(sdiv(signedCoefficient, signedCoefficientA), signedCoefficientB)),
iszero(eq(sub(exponent, exponentA), exponentB))
)
}
// If we did overflow, normalize and try again. Normalized values
// cannot overflow, so this will always succeed, provided the
// exponents are not out of bounds.
if (didOverflow) {
(signedCoefficientA, exponentA) = normalize(signedCoefficientA, exponentA);
(signedCoefficientB, exponentB) = normalize(signedCoefficientB, exponentB);
return multiply(signedCoefficientA, exponentA, signedCoefficientB, exponentB);
}
return (signedCoefficient, exponent);
}
}

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🔭 Outside diff range comments (3)
src/lib/table/LibLogTable.sol (2)

10-34: 🧹 Nitpick (assertive)

Prefer deriving row‑count & byte‑size from table.length instead of hard‑coding magic numbers

The assembly initialises i with mul(0x20, 90) and stores 1820 as the payload size.
Those literals tie the helper tightly to a 91‑row table; future size tweaks will silently corrupt the copy or mis‑encode the payload.

Consider:

-            mstore(0x40, add(encoded, add(1820, 0x20)))
+            let rows := mload(table)          // 91
+            let bytesPerRow := 0x14           // 10 * 2
+            mstore(0x40, add(encoded, add(mul(rows, bytesPerRow), 0x20)))-                let i := add(table, mul(0x20, 90))
+                let i := add(table, mul(0x20, sub(rows, 1)))

This removes two magic numbers and guarantees correctness if the table is extended again.


66-92: ⚠️ Potential issue

toBytes(uint8[10][101]): cursor decrements by 1 while writing 32 bytes

The loop:

cursor := sub(cursor, 1)
mstore(cursor, mload(...))   // writes 32 bytes

causes each 32‑byte chunk to overlap the previous by 31 bytes, duplicating data and overshooting encoded.
It worked accidentally before because the consumer only read the first byte per chunk, but it is extremely brittle.

Either copy one byte at a time or advance the cursor by 32:

-                cursor := sub(cursor, 1)
+                cursor := sub(cursor, 0x20)

Review the two other uint8 helpers – they exhibit the same pattern.

src/lib/implementation/LibDecimalFloatImplementation.sol (1)

725-753: ⚠️ Potential issue

withTargetExponent silently returns 0 for large positive exponentDiff

For targetExponent > exponent the function emits 0 instead of reverting when exponentDiff > 76.
The caller cannot distinguish between a legitimate zero and a scaling failure, which can corrupt results (e.g. power10 exponent calculation).

Recommend consistent behaviour:

-                if (exponentDiff > 76 || exponentDiff < 0) {
-                    return (NORMALIZED_ZERO_SIGNED_COEFFICIENT);
-                }
+                if (exponentDiff > 76 || exponentDiff < 0) {
+                    revert WithTargetExponentOverflow(signedCoefficient, exponent, targetExponent);
+                }
♻️ Duplicate comments (10)
test/src/lib/implementation/LibDecimalFloatImplementation.power10.t.sol (3)

54-60: 🧹 Nitpick (assertive)

Improve readability of large numeric literals.

Using scientific notation literals like 5.012531328320802005012531328320802005e37 makes the code hard to read and maintain. Scientific notation can also introduce subtle rounding issues when the compiler interprets these as rational numbers.

Consider using underscore-separated integer constants:

-        checkPower10(-0.3e37, -37, 5.012531328320802005012531328320802005e37, -38);
+        int256 constant COEFF = 5_012_531_328_320_802_005_012_531_328_320_802_005e19;
+        checkPower10(-0.3e37, -37, COEFF, -38);

This approach makes the code more readable and guarantees the value is treated as an exact int256.


62-67: 🧹 Nitpick (assertive)

Improve readability of large numeric literals.

Same issue as above with scientific notation literals in interpolated lookups tests.

Consider using underscore-separated integer constants for large numbers to improve readability and prevent compiler rounding:

-        checkPower10(1.55555e37, -37, 35935e37, -40);
+        int256 constant LOOKUP_COEFF = 35_935e37;
+        checkPower10(1.55555e37, -37, LOOKUP_COEFF, -40);

71-77: ⚠️ Potential issue

Fix potential reversion in boundFloat before vm.assume filters run.

The packLossless function can revert if x is out of range, but x isn't constrained before calling the function. This could cause the fuzzer to abort test runs rather than discard invalid cases.

Apply this fix to bound x before attempting to pack:

 function boundFloat(int224 x, int32 exponent) internal pure returns (int224, int32) {
     exponent = int32(bound(exponent, -76, 76));
+    x = int224(bound(x, -1e38 + 1, 1e38 - 1)); // prevent coefficient overflow
     Float a = LibDecimalFloat.packLossless(x, exponent);
     vm.assume(a.gt(LibDecimalFloat.packLossless(-1e38, 0)));
     vm.assume(a.lt(LibDecimalFloat.packLossless(type(int224).max, 0)));
     return (x, exponent);
 }
test/src/lib/LibDecimalFloat.power10.t.sol (2)

11-13: 🧹 Nitpick (assertive)

Add view modifier to the power10External function.

This function only reads from state (via logTables()) and performs computations without modifying state. Adding the view modifier would communicate this intent, provide minor gas savings, and prevent accidental state modifications.

-function power10External(int256 signedCoefficient, int256 exponent) external returns (int256, int256) {
+function power10External(int256 signedCoefficient, int256 exponent) external view returns (int256, int256) {
    return LibDecimalFloatImplementation.power10(logTables(), signedCoefficient, exponent);
}

15-17: 🧹 Nitpick (assertive)

Add view modifier to the Float-based power10External function.

For consistency with the other power10External function and to accurately represent the function's behavior, add the view modifier here as well.

-function power10External(Float float) external returns (Float) {
+function power10External(Float float) external view returns (Float) {
    return LibDecimalFloat.power10(logTables(), float);
}
src/lib/implementation/LibDecimalFloatImplementation.sol (2)

109-145: Recursive overflow‑retry in multiply persists – replace with single‑pass normalisation
This was flagged previously and is still present.

Each overflow triggers an extra stack frame and ~2× gas. Pre‑normalising operands before multiplication is simpler and cheaper.


319-324: 🧹 Nitpick (assertive)

10 ** alignmentExponentDiff is gas‑heavy; use a tiny lookup table

alignmentExponentDiff ≤ 37, so a uint256[38] POW10 constant saves ~200 gas per add.

-            multiplier = 10 ** alignmentExponentDiff;
+            multiplier = POW10[alignmentExponentDiff];

(Initialisation cost is paid once at deployment.)

src/lib/LibDecimalFloat.sol (3)

88-89: using directive is still redundant inside the defining library

This directive has no effect inside the same library that declares the functions (library methods are always in scope here) and slightly increases byte‑code size.
Removing it keeps the byte‑code lean and avoids confusing future readers.


343-345: A short comment would save future head‑scratching

signextend(27, …) is mathematically correct because byte 27 holds bit 223 (the sign bit of the 224‑bit coefficient). Documenting this “magic number” makes the intention crystal‑clear for auditors and contributors.


355-363: Unused lossless variable – use _ to signal its intentional discard

Storing then discarding the flag is fine, but replacing it with the unnamed tuple slot keeps the byte‑code a little smaller and silences some linters:

-        (Float c, bool lossless) = packLossy(signedCoefficient, exponent);
-        // Addition can be lossy.
-        (lossless);
+        (Float c, ) = packLossy(signedCoefficient, exponent); // addition can be lossy

Apply the same pattern to sub, minus, abs, multiply, divide, inv, frac, floor, power10, log10, and power.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0094e7d and 6fd3a74.

⛔ Files ignored due to path filters (1)
  • src/generated/LogTables.pointers.sol is excluded by !**/generated/**
📒 Files selected for processing (8)
  • src/lib/LibDecimalFloat.sol (14 hunks)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (5 hunks)
  • src/lib/table/LibLogTable.sol (11 hunks)
  • test/src/lib/LibDecimalFloat.power.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.power10.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.sub.t.sol (1 hunks)
  • test/src/lib/format/LibFormatDecimalFloat.t.sol (2 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.power10.t.sol (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: git-clean
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
🔇 Additional comments (16)
test/src/lib/format/LibFormatDecimalFloat.t.sol (4)

39-40: Address the CoefficientOverflow by using appropriate bounds.

The current code now bounds the value to uint256(int256(type(int224).max)), which is more appropriate than the previous unbounded approach, as it prevents the CoefficientOverflow error mentioned in past reviews.


37-44: LGTM: Renamed function and updated implementation for packed Float representation.

The function has been appropriately renamed from testRoundTrip to testFormatDecimalRoundTrip and updated to use the new packed Float representation. The implementation correctly uses fromFixedDecimalLosslessPacked and handles the parsed result as a tuple.


25-33: LGTM: Updated test to handle packed Float representation.

The test has been properly updated to unpack the Float value before calling the external formatting function, reflecting the shift to packed representation while maintaining the same test logic.


20-22: LGTM: Updated function signature for packed Float type.

Function signature correctly changed to accept Float as a value parameter instead of a memory reference, consistent with the codebase-wide shift to packed representation.

test/src/lib/implementation/LibDecimalFloatImplementation.power10.t.sol (4)

12-23: LGTM: Well-structured helper function for power10 tests.

The checkPower10 function is well-designed to simplify test assertions with clean parameter passing and explicit error messages for each assertion.


79-83: LGTM: Fuzz test ensures no reversion within valid ranges.

The fuzz test appropriately uses the boundFloat helper to constrain inputs, ensuring the function doesn't revert for valid inputs.


25-33: LGTM: Comprehensive test coverage for exact powers.

The test cases cover important power of 10 scenarios with appropriate normalization checks.


35-60: LGTM: Good coverage of exact lookup values.

The test cases thoroughly verify both integer and fractional powers across the expected range of the lookup table.

test/src/lib/LibDecimalFloat.sub.t.sol (3)

17-18: LGTM: Updated to use the implementation library.

Correctly updated to call LibDecimalFloatImplementation.sub instead of LibDecimalFloat.sub, aligning with the refactoring approach.


20-22: LGTM: Updated function signature for packed Float type.

Function signature correctly changed to accept and return Float as value parameters instead of memory references, consistent with the codebase-wide shift to packed representation.


24-38: LGTM: Renamed and restructured test for packed representation.

The test function has been appropriately renamed from testSubMem to testSubPacked and restructured to handle the packed representation, with proper unpacking and comparison logic.

test/src/lib/LibDecimalFloat.power10.t.sol (2)

19-37: LGTM: Improved test with explicit exponent overflow handling.

The test function has been renamed to testPower10Packed and now properly handles the exponent overflow case with explicit assertions. The unpacking and comparison logic correctly validates the results.


12-12: LGTM: Updated to use the implementation library.

Correctly updated to call LibDecimalFloatImplementation.power10 instead of LibDecimalFloat.power10, aligning with the refactoring approach.

test/src/lib/LibDecimalFloat.power.t.sol (1)

37-38: Extremely large expected exponent looks suspicious

expectedExponent = 60907 is five orders of magnitude larger than any exponent produced elsewhere in the suite.
Was this intended, or is the coefficient/exponent pair swapped? A typo here will make the test vacuous because the assertEq will always fail and fall into the try/catch path later.

src/lib/LibDecimalFloat.sol (2)

150-152: 🧹 Nitpick (assertive)

Same shadowing issue as above (float vs. Float)

Renaming here keeps the codebase consistent and readable.

-        (int256 signedCoefficient, int256 exponent) = fromFixedDecimalLossless(value, decimals);
-        return packLossless(signedCoefficient, exponent);
+        (int256 signedCoefficient, int256 exponent) = fromFixedDecimalLossless(value, decimals);
+        return packLossless(signedCoefficient, exponent);

(The return value is already anonymous; the local variable can be removed entirely.)

Likely an incorrect or invalid review comment.


291-320:

Details

❌ Incorrect review comment

Mask the exponent before shifting to guarantee a clean 32‑bit payload

shl(0xe0, exponent) relies on the fact that exponent has been range‑checked to fit an int32, but its high bits are still set for negative numbers.
Although the resulting top‑32‑bits are correct two’s‑complement, explicitly masking communicates intent and removes any possibility of stray bits leaking in if the range check ever changes.

-            assembly ("memory-safe") {
-                float := or(and(signedCoefficient, mask), shl(0xe0, exponent))
-            }
+            assembly ("memory-safe") {
+                // Cast + mask keeps only the low 32‑bit two’s‑complement representation.
+                let packedExponent := and(exponent, 0xffffffff)
+                float := or(and(signedCoefficient, mask), shl(0xe0, packedExponent))
+            }

This is a micro‑optimisation (1 extra AND) but improves auditability and future‑proofs the packing logic.


Unnecessary explicit masking of the exponent

The Yul shl operator shifts left modulo 2²⁵⁶, discarding any high bits beyond the 256‑bit word. Combined with your prior int32 range‐check, the shifted result already contains exactly the 32‑bit two’s‑complement exponent in bits 224–255. Adding an extra AND therefore does not change the outcome—it’s purely a stylistic clarification, not a functional necessity. If you want to document intent, a comment on the modulo semantics is sufficient without inserting the mask.

Likely an incorrect or invalid review comment.

Comment thread src/lib/table/LibLogTable.sol
Comment thread src/lib/table/LibLogTable.sol
Comment thread src/lib/table/LibLogTable.sol
Comment thread test/src/lib/LibDecimalFloat.power.t.sol
Comment thread test/src/lib/LibDecimalFloat.power.t.sol
Comment thread src/lib/implementation/LibDecimalFloatImplementation.sol
Comment thread src/lib/LibDecimalFloat.sol
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🔭 Outside diff range comments (2)
src/lib/table/LibLogTable.sol (1)

44-52: 💡 Verification agent

🧩 Analysis chain

mstore is writing 32 bytes but the cursor only advances 1 byte – high risk of data corruption

In the toBytes(uint8[10][91]) implementation the loop decrements the cursor by 1 (cursor := sub(cursor, 1)) yet each iteration writes 32 bytes with mstore.
This means every write overlaps (and therefore overwrites) the previous 31 bytes, so only the last byte copied per 32‑byte chunk ends up in encoded.
Unless this overlapping trick is deliberate and proven correct, it is very easy to introduce silent corruption the moment table sizes change or another engineer refactors the code.

  • Use mstore8 (and decrement the cursor by 1) when copying a single uint8.
  • Or, keep mstore but decrement the cursor by 32 to avoid overlap.
  • Add a unit test that round‑trips toBytes → decode → compare original table to catch regressions.
-                cursor := sub(cursor, 1)
-
-                mstore(cursor, mload(add(mload(i), j)))
+                cursor := sub(cursor, 1)           // keep for mstore8
+                let value := byte(0, mload(add(mload(i), j)))
+                mstore8(cursor, value)

🏁 Script executed:

#!/bin/bash
# Locate and inspect the toBytes implementation in LibLogTable.sol

# Show the function signature and a few lines around it
rg -n -C3 "function toBytes" src/lib/table/LibLogTable.sol

# Show the loop with mstore and cursor decrement to confirm the current behavior
rg -n -C3 "mstore(cursor" src/lib/table/LibLogTable.sol

Length of output: 1474


🏁 Script executed:

#!/bin/bash
# Display the uint8[10][91] toBytes implementation around lines 35–65
sed -n '35,65p' src/lib/table/LibLogTable.sol

Length of output: 964


Fix overlapping 32‑byte writes in toBytes(uint8[10][91])

In the Yul assembly loop you decrement the cursor by 1 byte but call mstore (which writes 32 bytes), causing each write to overlap the previous 31 bytes. This will silently corrupt encoded if the table size or indexing ever changes.

Please update the loop in src/lib/table/LibLogTable.sol (lines ~44–52) as follows:

  • If you want to copy one uint8 at a time, extract the byte and use mstore8, keeping cursor := sub(cursor, 1).
  • If you intend to write full 32‑byte words, step the cursor by 32: cursor := sub(cursor, 0x20).

Also, add a unit test that round‑trips a full 91×10 table through toBytes → decode → compare original to catch regressions.

Suggested diff:

-for {
-    let i := add(table, mul(0x20, 90))
-    let j := mul(0x20, 9)
-} gt(cursor, encoded) {
-    cursor := sub(cursor, 1)
-    j := sub(j, 0x20)
-} {
-    mstore(cursor, mload(add(mload(i), j)))
+for {
+    let i := add(table, mul(0x20, 90))
+    let j := mul(0x20, 9)
+} gt(cursor, encoded) {
+    cursor := sub(cursor, 1)           // step by 1 byte
+    j := sub(j, 0x20)
+} {
+    // extract the single uint8 and write one byte
+    let byteVal := byte(0, mload(add(mload(i), j)))
+    mstore8(cursor, byteVal)
     if iszero(j) {
         i := sub(i, 0x20)
         j := mul(0x20, 10)
     }
 }
src/lib/implementation/LibDecimalFloatImplementation.sol (1)

38-53: ⚠️ Potential issue

Broken constant‑ordering – compilation fails before tests even run

NORMALIZED_JUMP_DOWN_THRESHOLD and NORMALIZED_JUMP_UP_THRESHOLD reference
SIGNED_NORMALIZED_MAX / SIGNED_NORMALIZED_MIN before those constants are
declared.
Solidity requires a constant to be declared above every place it is used,
otherwise the compiler raises “Identifier not found or not unique”.

-/// @dev Every value above or equal to this can jump down while normalizing
-/// without overshooting and causing unnecessary precision loss.
-int256 constant NORMALIZED_JUMP_DOWN_THRESHOLD = SIGNED_NORMALIZED_MAX * PRECISION_JUMP_MULTIPLIER;
-/// @dev Every value below this can jump up while normalizing without
-/// overshooting the normalized range.
-int256 constant NORMALIZED_JUMP_UP_THRESHOLD = SIGNED_NORMALIZED_MIN / PRECISION_JUMP_MULTIPLIER;
-
 /// @dev The minimum absolute value of a normalized signed coefficient.
 uint256 constant NORMALIZED_MIN = 1e37;
 int256 constant SIGNED_NORMALIZED_MIN = 1e37;
 /// @dev The maximum absolute value of a normalized signed coefficient.
 uint256 constant NORMALIZED_MAX = 1e38 - 1;
 int256 constant SIGNED_NORMALIZED_MAX = 1e38 - 1;
+
+/// @dev Every value above or equal to this can jump down while normalizing
+/// without overshooting and causing unnecessary precision loss.
+int256 constant NORMALIZED_JUMP_DOWN_THRESHOLD = SIGNED_NORMALIZED_MAX * PRECISION_JUMP_MULTIPLIER;
+/// @dev Every value below this can jump up while normalizing without
+/// overshooting the normalized range.
+int256 constant NORMALIZED_JUMP_UP_THRESHOLD = SIGNED_NORMALIZED_MIN / PRECISION_JUMP_MULTIPLIER;

Moving the MIN/MAX definitions above the derived thresholds (or vice‑versa)
unblocks compilation.

♻️ Duplicate comments (9)
test/src/lib/LibDecimalFloat.decimal.t.sol (1)

31-41: Test updated with proper bounds to prevent overflow

The test function (renamed from testFromFixedDecimalLossyMem to testFromFixedDecimalLossyPacked) now properly:

  1. Bounds the input value to prevent coefficient overflow errors
  2. Uses the packed API fromFixedDecimalLossyPacked
  3. Unpacks the result to verify coefficient and exponent values
  4. Checks losslessness consistently

This addresses the previous overflow issue mentioned in past reviews and correctly tests the packed representation.

test/src/lib/LibDecimalFloat.inv.t.sol (1)

22-22: 🧹 Nitpick (assertive)

Update function name to reflect packed implementation

While the test logic has been correctly updated to work with packed Float values, the function name "testInvMem" suggests it's testing memory-based implementation.

Rename the function to better reflect its purpose:

-function testInvMem(Float float) external {
+function testInvPacked(Float float) external {
src/lib/table/LibLogTable.sol (2)

70-80: Same single‑byte overlap issue repeated in toBytes(uint8[10][101])

The logic mirrors the previous function and ships with the same 1‑byte / 32‑byte overlap hazard.
Please fix both implementations together (see previous comment for details) or extract a common, well‑tested helper.


350-352: Placeholder comment still contradicts the row content

Previous reviews already pointed this out: the comment claims “The last entry is 10000, so we just use that” but the placeholder row in logTableDecSmall is all zeros.
Either change the numbers to 10000 or update the comment so future readers are not mis‑led.

test/src/lib/LibDecimalFloat.power.t.sol (2)

32-33: Negative coefficients logged as huge unsigned numbers

Casting signedCoefficientA to uint256 underflows when the value is negative, producing unreadable log output.

-console2.log("%d %d Gas used: %d", uint256(signedCoefficientA), uint256(exponentA), …);
+console2.log("%d %d Gas used: %d", signedCoefficientA, exponentA, …);

79-94: catch {} blocks silently swallow all reverts – hides genuine failures

The fuzz test ignores every error, including assertion failures, overflow checks and unexpected revert reasons.
At minimum assert that only the known, acceptable selectors are swallowed (e.g. CoefficientOverflow, ExponentOverflow, division‑by‑zero).

-        } catch (bytes memory err) {}
+        } catch (bytes memory err) {
+            // Only overflow‑related errors are acceptable; anything else is a failure.
+            bytes4 sel;
+            assembly { sel := mload(add(err, 0x20)) }
+            if (sel != CoefficientOverflow.selector && sel != ExponentOverflow.selector) {
+                fail("unexpected revert");
+            }
+        }

This keeps the fuzz test meaningful and prevents silent false‑negatives.

src/lib/implementation/LibDecimalFloatImplementation.sol (1)

535-538: ⚠️ Potential issue

Unchecked exponent addition can still overflow (power10)

This is the exact issue flagged in the previous review and it remains
unaddressed – adding the characteristic shift to exponent is done in an
unchecked context, so type(int256).max can silently wrap.

return (
    signedCoefficient,
-   1 + exponent + withTargetExponent(characteristicCoefficient, characteristicExponent, 0)
+   normalize(                 // <- cheap guard, no precision loss
+       1,
+       exponent + withTargetExponent(characteristicCoefficient, characteristicExponent, 0)
+   ).exponent
);

Either normalise the result before returning or insert an explicit range
check.

test/src/lib/LibDecimalFloat.lt.t.sol (1)

99-105: 🛠️ Refactor suggestion

Duplicate source of undefined Floats (see above)

a and b are unconstrained random bytes. Convert them from bounded ints to
keep the property‑based test meaningful and revert‑free.

test/src/lib/LibDecimalFloat.gt.t.sol (1)

92-98: 🛠️ Refactor suggestion

Random Float inputs again – may bypass test intent

The three‑way compare relies on numeric properties. If either packed value
is invalid the test reverts before the assertions run, giving a false sense of
security.

Adopt the same bounded‑int strategy as suggested for the lt tests.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6fd3a74 and c740efc.

📒 Files selected for processing (16)
  • .gas-snapshot (1 hunks)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (7 hunks)
  • src/lib/table/LibLogTable.sol (11 hunks)
  • test/src/lib/LibDecimalFloat.add.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.decimal.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.decimalLossless.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.divide.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.floor.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.frac.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.gt.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.inv.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.log10.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.lt.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.multiply.t.sol (2 hunks)
  • test/src/lib/LibDecimalFloat.pack.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.power.t.sol (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: git-clean
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
🔇 Additional comments (32)
test/src/lib/LibDecimalFloat.pack.t.sol (4)

4-4: Import statement updated to reflect new Float type definition

The import statement has been updated to include ExponentOverflow, Float, and EXPONENT_MAX from the LibDecimalFloat.sol file. This supports the codebase's consolidation around a single packed Float type alias.


9-11: Function updated for packed Float type

The function has been updated to return a Float type along with a boolean indicating losslessness. This correctly implements the updated API for packing operations using the consolidated Float type.


13-15: Unpacking function updated for consistency

The unpacking function now accepts a Float parameter directly instead of a separate PackedFloat type, maintaining consistency with the new consolidated type system.


19-24: Test refactored to use the new packed Float API

The test now correctly:

  1. Calls packLossy to create a packed Float
  2. Verifies losslessness before proceeding
  3. Unpacks the Float and verifies coefficient and exponent values

The test properly validates the round-trip behavior with the new consolidated type.

test/src/lib/LibDecimalFloat.divide.t.sol (3)

4-5: Imports simplified to match consolidated type system

The imports have been updated to focus on the core Float type and the implementation library, removing unnecessary imports and reflecting the codebase's consolidation around a single packed Float type.


24-26: Function signature updated for value-based parameter passing

The function now takes Float parameters by value instead of by memory reference, which is more efficient for the packed Float type (likely a bytes32 alias).


28-43: Test function refactored for packed Float testing

The test has been renamed from testDivideMem to testDividePacked and updated to:

  1. Unpack Float inputs to get raw values
  2. Test division using the implementation directly
  3. Compare against the packed Float implementation
  4. Properly handle error cases

This ensures the packed and unpacked implementations produce identical results, validating the refactoring approach.

test/src/lib/LibDecimalFloat.decimal.t.sol (2)

27-29: Function updated to use packed Float parameter

The function now takes a Float parameter by value instead of by memory reference, aligning with the consolidated type system and improving efficiency for this small value type.


45-58: Test refactored for packed Float consistency verification

The test function (renamed from testToFixedDecimalLossyMem to testToFixedDecimalLossyPacked) properly:

  1. Unpacks the Float parameter to call the implementation with raw values
  2. Compares results between packed and unpacked implementations
  3. Handles error propagation consistently

This ensures both implementations behave identically across all inputs.

test/src/lib/LibDecimalFloat.multiply.t.sol (3)

4-5: Imports updated for consolidated Float type

The imports have been simplified to focus on the core Float type and constants, aligning with the codebase's consolidated approach to floating-point representation.


24-26: Function signature updated for value-based parameter passing

The function now takes Float parameters by value instead of by memory reference, which is more efficient for this packed type and aligns with the consolidated approach.


28-43: Test refactored for packed Float verification

The test function (renamed from testMultiplyMem to testMultiplyPacked) properly:

  1. Unpacks Float inputs to get raw component values
  2. Tests multiplication using direct component values
  3. Compares against multiplication using packed Floats
  4. Handles error cases consistently

This ensures both implementation approaches produce identical results, validating the refactoring strategy.

test/src/lib/LibDecimalFloat.inv.t.sol (4)

5-6: LGTM! Import changes aligned with the consolidated packed Float approach

The imports have been correctly updated to reflect the new architecture with packed Float type and delegation to implementation library.


11-16: LGTM! Function correctly adapts to the packed Float architecture

The function now properly:

  1. Delegates core logic to LibDecimalFloatImplementation.inv
  2. Uses packLossy to convert the returned values to a packed Float
  3. Returns the packed Float type directly

This aligns well with the consolidated packed approach.


18-20: LGTM! Updated function signature to use packed Float

Correctly updated to use the packed Float type by value rather than by memory reference.


23-29: LGTM! Test logic properly updated for packed Float

The test logic has been correctly updated to:

  1. Unpack the input Float to get components
  2. Compare unpacked components instead of struct fields
  3. Handle both the component-based and packed Float paths consistently

This ensures equivalence between the two paths is properly tested.

test/src/lib/LibDecimalFloat.log10.t.sol (4)

5-5: LGTM! Import updated to include implementation library

Correct addition of the implementation library import that contains the core logarithm logic.


11-17: LGTM! Function correctly updated to use packed Float

The function has been properly updated to:

  1. Call the implementation library's log10 function
  2. Pack the result using packLossy
  3. Return a packed Float value

This aligns with the library's shift to packed representation.


19-21: LGTM! Updated function signature to use packed Float directly

The function now correctly accepts a packed Float by value rather than by memory reference and returns a packed Float.


23-30: LGTM! Test renamed and updated for packed Float

The test function has been correctly:

  1. Renamed from testLog10Mem to testLog10Packed to reflect the type change
  2. Updated to work with packed Float values
  3. Modified to unpack and compare components instead of comparing struct fields

This ensures consistency with the new packed Float approach.

test/src/lib/LibDecimalFloat.decimalLossless.t.sol (6)

21-23: LGTM! Function renamed and updated to use packed Float

The function has been correctly renamed from fromFixedDecimalLosslessMemExternal to fromFixedDecimalLosslessPackedExternal and updated to return a packed Float.


33-35: LGTM! Updated function signature to use packed Float

The function now correctly accepts a packed Float by value rather than by memory reference.


37-38: LGTM! Value bounded to prevent coefficient overflow

The test now properly bounds the input value to stay within int224 range, preventing coefficient overflow errors.


45-49: LGTM! Test updated to use packed Float representation

The test has been properly updated to:

  1. Use the packed Float implementation fromFixedDecimalLosslessPacked
  2. Unpack the Float to get components
  3. Compare unpacked components instead of struct fields

This ensures consistency with the new packed Float approach.


52-54: LGTM! Test renamed and updated for packed Float

The test function has been correctly renamed from testToFixedDecimalLosslessMem to testToFixedDecimalLosslessPacked and updated to work with packed Float values.


59-59: LGTM! Updated external call to use packed Float

Correctly updated to use the external function with packed Float parameter.

test/src/lib/LibDecimalFloat.add.t.sol (4)

4-8: LGTM! Imports updated for packed Float and implementation

The imports have been correctly updated to:

  1. Import the packed Float type
  2. Import the implementation library and relevant constants
  3. Remove unused constants

This properly reflects the architectural shift to packed Float representation.


15-25: LGTM! Function updated to return packed Float

The function has been properly updated to:

  1. Return a packed Float instead of component tuple
  2. Call the implementation library's add function
  3. Pack the result using packLossy

This aligns with the library's packed Float approach.


27-29: LGTM! Updated function signature to use packed Float

The function now correctly accepts packed Float values by value rather than by memory reference and returns a packed Float.


31-43:

Details

❌ Incorrect review comment

Consider implementing coefficient overflow handling in testAddPacked

While the test logic has been updated to use packed Float values, it doesn't explicitly handle cases where the result of addition causes coefficient overflow.

From past review comments, it appears there might be issues with coefficient overflow errors. Consider adding bounds checking or pre-filtering input values to avoid CoefficientOverflow errors.

 function testAddPacked(Float a, Float b) external {
+    // Filter out Float values that could cause overflow
+    vm.assume(Float.unwrap(a) != 0 && Float.unwrap(b) != 0);
+    {
+        try LibDecimalFloat.unpack(a) returns (int256 sc, int256 e) {
+            vm.assume(sc >= type(int224).min && sc <= type(int224).max);
+        } catch {
+            return;
+        }
+    }
+    {
+        try LibDecimalFloat.unpack(b) returns (int256 sc, int256 e) {
+            vm.assume(sc >= type(int224).min && sc <= type(int224).max);
+        } catch {
+            return;
+        }
+    }
     (int256 signedCoefficientA, int256 exponentA) = LibDecimalFloat.unpack(a);
     (int256 signedCoefficientB, int256 exponentB) = LibDecimalFloat.unpack(b);

🏁 Script executed:

#!/bin/bash
# Check for coefficient overflow tests or handling in the tests or implementation
echo "Searching for coefficient overflow handling in implementation..."
rg -A 5 "CoefficientOverflow" src/

echo "Searching for coefficient bounds in tests..."
rg -A 5 "bound.*coefficient" test/

Length of output: 1446


Remove Unnecessary Overflow Filters in testAddPacked

The existing testAddPacked already covers both successful additions and overflow reverts by using the try/catch block and asserting that reverts match the original error. Introducing pre‑filters to exclude overflow‑causing inputs would hide those error cases and reduce test coverage. No additional bounds checks or vm.assume calls for coefficient ranges are needed here.

Likely an incorrect or invalid review comment.

test/src/lib/LibDecimalFloat.floor.t.sol (1)

11-13:

Details

❌ Incorrect review comment

Same fuzz issue as in frac test

testFloorNotReverts assumes every random Float returned by the fuzzer is well‑formed. Please guard against invalid encodings or switch the property to “does not revert iff valid”.


🏁 Script executed:

#!/bin/bash
# Show the beginning of the floor test to confirm if any guards are applied
sed -n '1,50p' test/src/lib/LibDecimalFloat.floor.t.sol

# Locate and inspect the frac test for comparison
rg -n 'testFracNotReverts' -n test/src/lib
sed -n '1,50p' test/src/lib/LibDecimalFloat.frac.t.sol

Length of output: 4088


🏁 Script executed:

#!/bin/bash
# Locate the definition of Float and related constants in the library
rg -n "type Float" -n src/lib/LibDecimalFloat.sol || rg -n "struct Float" -n src/lib/LibDecimalFloat.sol

# Show surrounding lines to understand validity constraints
rg -C3 "packLossless" -n src/lib/LibDecimalFloat.sol

Length of output: 845


🏁 Script executed:

#!/bin/bash
# Search and display the implementation of `floor` in the library
rg -n "function floor" -n src/lib/LibDecimalFloat.sol

# If found, print its body for inspection
rg -C5 "function floor" -n src/lib/LibDecimalFloat.sol

Length of output: 734


🏁 Script executed:

#!/bin/bash
# Find the unpack function implementation for Float
rg -n "function unpack" -n src/lib/LibDecimalFloat.sol

# Display its body for analysis
rg -C5 "function unpack" -n src/lib/LibDecimalFloat.sol

Length of output: 832


🏁 Script executed:

#!/bin/bash
# Search for the packLossy function definition
rg -n "function packLossy" -n src/lib/LibDecimalFloat.sol

# Display its implementation for analysis
rg -C5 "function packLossy" -n src/lib/LibDecimalFloat.sol

Length of output: 932


🏁 Script executed:

#!/bin/bash
# Locate the file(s) defining `characteristicMantissa`
rg -l "characteristicMantissa" -n src

# Display the implementation for inspection
for file in $(rg -l "characteristicMantissa" -n src); do
  echo "=== $file ==="
  rg -C8 "function characteristicMantissa" -n "$file"
done

Length of output: 1325


Incorrect suggestion: floor never reverts on any Float encoding

The floor implementation unpacks an arbitrary 32‑byte value, computes the characteristic/mantissa in an unchecked block, and repacks via packLossy (which does not revert). There is no code path in floor that can throw, even for “invalid” bit patterns. As such, testFloorNotReverts correctly asserts that calling x.floor() never reverts, and no additional guards or “iff valid” checks are required.

Likely an incorrect or invalid review comment.

.gas-snapshot (1)

121-128:

Details

❓ Verification inconclusive

power10 fuzz tests now > 1.23 M gas – confirm no accidental regression

LibDecimalFloatPower10Test:testPower10Packed, testExactLookups, and testInterpolatedLookupsPower have all crossed the 1.6 M / 1.23 M gas mark.
Because these values changed together with the packed‑float refactor, please sanity‑check:

  1. The packing/unpacking logic is unchecked where safe;
  2. No redundant abi.decode/abi.encodePacked calls were introduced;
  3. Re‑normalisation is skipped when the exponent is already in range.

If the spike is unavoidable, consider adding a “budget” comment in the test explaining the rationale so reviewers do not flag it again.


🏁 Script executed:

#!/bin/bash
# Locate Power10Test entries in the gas snapshot
grep -n 'LibDecimalFloat.*Power10Test' .gas-snapshot
grep -n 'testPower10Packed' .gas-snapshot
grep -n 'testExactLookups' .gas-snapshot
grep -n 'testInterpolatedLookupsPower' .gas-snapshot

Length of output: 984


🏁 Script executed:

#!/bin/bash
# Search for abi.encodePacked and abi.decode occurrences
rg -n "abi\.encodePacked" .
rg -n "abi\.decode" .

# Search for unchecked usage in Solidity files
rg -n "unchecked" --glob "*.sol"

# Search for normalization-related functions or calls
rg -n "normalize" --glob "*.sol"
rg -n "renormalize" --glob "*.sol"

Length of output: 10716


Please verify packing logic and re‑normalization in Power10 implementation

Quick findings from the codebase search:

- No abi.decode calls and only two abi.encodePacked usages (in parsing tests and deployment), so it’s unlikely redundant encode/decode was introduced.
- Numerous unchecked { … } blocks exist in LibDecimalFloatImplementation.sol, but it’s not obvious which cover the new pack/unpack paths in your Power10 refactor.
- The early‐exit “exponent already in range” check around normalization in the Power10 path needs to be confirmed present.

Next steps:

• Open the new Power10 routines in src/lib/implementation/LibDecimalFloatImplementation.sol and ensure:
  – Packing/unpacking calls are wrapped in unchecked only where safe.
  – No extra abi.encodePacked/abi.decode are invoked in that code path.
  – A guard skips any re‑normalization when the exponent is within the acceptable range.

If the gas increase is unavoidable, please add a “budget” comment in the Power10 test explaining the spike.

Comment thread test/src/lib/LibDecimalFloat.divide.t.sol
Comment thread test/src/lib/LibDecimalFloat.multiply.t.sol
Comment thread src/lib/table/LibLogTable.sol
Comment thread test/src/lib/LibDecimalFloat.frac.t.sol
Comment thread test/src/lib/LibDecimalFloat.lt.t.sol
Comment thread test/src/lib/LibDecimalFloat.gt.t.sol
Comment thread .gas-snapshot
Comment thread .gas-snapshot
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
test/src/lib/LibDecimalFloat.power.t.sol (1)

81-96: Empty catch blocks still swallow unexpected reverts

Previous reviews highlighted that silent catches hide assertion failures.
Lines 82-96 still ignore all errors other than an implicit DivisionByZero, so a genuine bug inside power could pass unnoticed.

Repurpose the handler to allow only the specific, documented revert selectors (e.g. ExponentOverflow, DivisionByZero) and fail otherwise.

This repeats earlier feedback verbatim.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c740efc and e33ee75.

📒 Files selected for processing (1)
  • test/src/lib/LibDecimalFloat.power.t.sol (2 hunks)
🔇 Additional comments (1)
test/src/lib/LibDecimalFloat.power.t.sol (1)

48-61: Potential division-by-zero in checkRoundTrip

roundTrip may legitimately evaluate to 0 (e.g. when a.power(b) underflows).
Unlike the fuzz helper, this path isn’t guarded here, so a.divide(roundTrip) will revert with DivisionByZero.

Add the same zero-check you already use in testRoundTripFuzz:

-        Float c = a.power(b, tables);
-
-        Float roundTrip = c.power(b.inv(), tables);
+        Float c = a.power(b, tables);
+
+        Float roundTrip = c.power(b.inv(), tables);
+        if (roundTrip.isZero()) return; // avoid divide-by-zero

Comment thread test/src/lib/LibDecimalFloat.power.t.sol Outdated
Comment thread test/src/lib/LibDecimalFloat.power.t.sol
@thedavidmeister thedavidmeister merged commit 7c21e1e into main Apr 28, 2025
4 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 10, 2025
4 tasks
This was referenced Jul 19, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Aug 11, 2025
Merged
4 tasks
This was referenced Aug 17, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Sep 16, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant