Skip to content

full precision div#105

Merged
thedavidmeister merged 18 commits intomainfrom
2025-08-15-minimize-denominator-div
Aug 20, 2025
Merged

full precision div#105
thedavidmeister merged 18 commits intomainfrom
2025-08-15-minimize-denominator-div

Conversation

@thedavidmeister
Copy link
Copy Markdown
Contributor

@thedavidmeister thedavidmeister commented Aug 16, 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

    • Division/inverse use a consistent higher-precision scaling (effective scale -76) and a new public MulDivOverflow error is exposed.
  • Bug Fixes

    • Reduced intermediate arithmetic overflow risk and improved handling of extreme signed-edge cases.
  • Tests

    • Many test expectations, constants and some test names updated; new reciprocal/overflow tests added and gas snapshot metrics refreshed.
  • Documentation

    • Clarified notes around inverse and pow10 behavior under the new precision model.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 16, 2025

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between 3aa0590 and b87ef83.

📒 Files selected for processing (1)
  • crates/float/src/lib.rs (1 hunks)
 __________________________________________________________________________________________________________
< Minimize coupling between modules. Avoid coupling by writing 'shy' code and applying the Law of Demeter. >
 ----------------------------------------------------------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).

Walkthrough

Adds a public error MulDivOverflow, implements an internal 512-bit mulDiv helper and refactors div/inv to scale coefficients into ~1e75–1e76 and use mulDiv; updates tests, constants, and gas snapshots to match new scaling and adds a test asserting the new overflow revert.

Changes

Cohort / File(s) Summary
Errors
src/error/ErrDecimalFloat.sol
Adds public error MulDivOverflow(uint256 x, uint256 y, uint256 denominator) after ZeroNegativePower.
Core implementation
src/lib/implementation/LibDecimalFloatImplementation.sol
Imports MulDivOverflow; adds internal mulDiv(uint256,uint256,uint256) implementing 512-bit mul/div with modular-inverse division and revert on overflow; refactors div to compute signs/abs, maximize coefficients into ~1e75–1e76, choose scale (1e76 with 1e75 fallback via adjustExponent), use mulDiv for quotient, and update sign/exponent; refactors inv to call div(1e76, -76, ...).
Test constants
test/lib/LibCommonResults.sol
Extends literal lengths for ONES and THREES; adds new int256 THREES_PACKED.
Mixed tests
test/src/lib/LibDecimalFloat.mixed.t.sol
Imports THREES_PACKED instead of THREES; renames testDiv1Over3()testDiv1Over3Mixed(); updates expected coefficient/exponent assertions.
Division tests
test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol
Updates expected coefficients/exponents to the new -76 normalization across many cases; adds (1/3)/(1/9) case; adjusts fuzz loop initialization for new scaling.
Pow tests
test/src/lib/LibDecimalFloat.pow.t.sol
Replaces two assertions with new expected magnitudes/exponents shifted to the new scale; adds explanatory comments; rest unchanged.
Pow10 tests (public)
test/src/lib/LibDecimalFloat.pow10.t.sol
After unpacking pow10 result, repacks with packLossy and re-unpacks, then asserts equality to account for packing behavior.
Pow10 tests (implementation)
test/src/lib/implementation/LibDecimalFloatImplementation.pow10.t.sol
Renames testExactLookups()testExactLookupsPow10(); updates expected lookup result scaling/literal for -0.3e37.
Inv tests
test/src/lib/implementation/LibDecimalFloatImplementation.inv.t.sol
Adds invExternal wrapper and testInv0() which expects MulDivOverflow revert; imports MulDivOverflow for selector.
Crate tests
crates/float/src/lib.rs
Adjusts test expectation to assert DecimalFloat(DecimalFloatErrors::MulDivOverflow(_)) when dividing by zero; adds a dbg! of the error in the test.
Gas snapshot
.gas-snapshot
Updates many gas metrics and test-name mappings to reflect renamed tests and changed gas usage across DecimalFloat test suites.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant LibDecimalFloatImpl
  participant mulDiv

  Caller->>LibDecimalFloatImpl: div(Float A, Float B)
  LibDecimalFloatImpl->>LibDecimalFloatImpl: compute signs, abs vals, maximize coefficients (≈1e75–1e76), pick scale and adjustExponent
  LibDecimalFloatImpl->>mulDiv: mulDiv(absCoeffA, scale, absCoeffB)
  alt mulDiv overflows
    mulDiv-->>LibDecimalFloatImpl: revert MulDivOverflow
  else success
    mulDiv-->>LibDecimalFloatImpl: quotient
    LibDecimalFloatImpl-->>Caller: return Float(coefficient=quotient, exponent=expA - expB - adjustExponent, sign=XOR)
  end
Loading
sequenceDiagram
  participant Caller
  participant LibDecimalFloatImpl

  Caller->>LibDecimalFloatImpl: inv(Float B)
  LibDecimalFloatImpl->>LibDecimalFloatImpl: call div(1e76, -76, signedCoefficient(B), exponent(B))
  LibDecimalFloatImpl-->>Caller: return Float(result)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • update div precision #104 — Modifies div/inv scaling and normalization; closely related to the new div/inv and mulDiv changes.

Suggested reviewers

  • hardyjosh
  • findolor

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 2025-08-15-minimize-denominator-div

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 9

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

111-130: Replace magic -76 with a named constant to aid readability.

Using a well-named constant avoids scattering -76 across tests and documents intent. For example, add and use a local constant:

-            int256 j = -76 - di;
+            // Adjust for the scaling exponent used by division normalization.
+            int256 constant ADJUST = -76;
+            int256 j = ADJUST - di;
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 87c8c65 and b29c945.

📒 Files selected for processing (8)
  • src/error/ErrDecimalFloat.sol (1 hunks)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (6 hunks)
  • test/lib/LibCommonResults.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.mixed.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.pow.t.sol (1 hunks)
  • test/src/lib/LibDecimalFloat.pow10.t.sol (1 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol (3 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.pow10.t.sol (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#59
File: crates/float/src/lib.rs:233-242
Timestamp: 2025-06-17T10:17:56.205Z
Learning: In the rainlanguage/rain.math.float repository, the maintainer 0xgleb prefers to handle documentation additions and improvements in separate issues rather than inline with feature PRs.
📚 Learning: 2025-06-17T10:02:01.394Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#64
File: src/concrete/DecimalFloat.sol:0-0
Timestamp: 2025-06-17T10:02:01.394Z
Learning: In the rain.math.float project, the Float type is designed with a specific bit layout: 224-bit signed coefficient and 32-bit signed exponent, represented as 32 bytes total. The LibDecimalFloat.unpack function returns values that are guaranteed to fit within int224 and int32 ranges due to this internal representation, making explicit range checks before casting unnecessary.

Applied to files:

  • test/src/lib/LibDecimalFloat.pow10.t.sol
📚 Learning: 2025-07-24T04:32:14.171Z
Learnt from: rouzwelt
PR: rainlanguage/rain.math.float#83
File: src/concrete/DecimalFloat.sol:248-251
Timestamp: 2025-07-24T04:32:14.171Z
Learning: In the rainlanguage/rain.math.float project, functions in DecimalFloat.sol that return tuples from LibDecimalFloat calls must unpack the tuple into local variables before returning them (rather than returning directly) to maintain compatibility with Slither static analysis checks.

Applied to files:

  • test/src/lib/LibDecimalFloat.pow10.t.sol
  • test/src/lib/LibDecimalFloat.mixed.t.sol
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: git-clean
🔇 Additional comments (10)
test/lib/LibCommonResults.sol (1)

4-6: ✅ int256 bounds verified for constants

• ONES: 76 digits
• THREES_PACKED: 67 digits
• THREES: 76 digits
(All are below the 77-digit safe threshold for int256.)

Recommendations

  • Add concise NatSpec comments above each constant to clarify the difference between THREES and THREES_PACKED and when to use each.
  • (Optional) Improve readability/maintenance by expressing these large literals via formulas (e.g., 10**76 / 9) or, once supported in your toolchain, using underscores in numeric literals.

Location: test/lib/LibCommonResults.sol (lines 4–6)
[optional_refactors_recommended]

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

35-35: Renaming to testExactLookupsPow10 improves discoverability

Good rename; the test name now clearly ties to pow10. No functional concerns.

test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol (2)

43-49: Good normalization to exponent -76 for 1/3 and -1/3.

The expectations line up with the new 1e76 scaling and sign handling. Tests are precise and deterministic.


59-75: Expectations for scaled divisions look consistent.

  • 1e18/3 → THREES at exponent -58 checks out given maximize() and adjustExponent=76.
  • 10/(1e38,-37) → 1 → 1e76,-76 is correct.
  • (1e38,-37)/2 → 5 → 5e75,-75 is consistent.
  • (5e37,-37)/(2e37,-37) → 2.5 → 2.5e76,-76 is consistent.

All cases match the new normalization strategy.

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

5-5: Switch to THREES_PACKED is appropriate for mixed/API-level tests.

Aligns expectations with packed representation used by the public API. Good move.


13-20: Renamed test and updated expectations look correct.

  • testDiv1Over3Mixed naming reads clearer.
  • Asserting THREES_PACKED and exponent -67 matches the mixed packing/unpacking semantics under the new scaling.
src/lib/implementation/LibDecimalFloatImplementation.sol (4)

4-4: Importing MulDivOverflow is appropriate and clarifies the revert surface.

Explicit revert type for mulDiv overflow paths improves diagnosability.


236-321: mulDiv helper is implemented correctly and follows the well-known pattern.

Good use of the 512-bit product and inverse modulo 2^256 with appropriate overflow checks and a dedicated revert type.

Note: The AI summary mentions that mul also uses mulDiv, but mul() above still uses coefficient multiply + normalize recursion. If aligning mul with the new 512-bit path is desired for symmetry and headroom, that can be a follow-up refactor.


199-233: Fallback branch is reachable for non-zero denominators

The maximize() routine only guarantees |B| ≥ 1e75—not ≥ 1e76—for all non-zero inputs. Coefficients in the range [1e75, 1e76) (e.g. 9×10⁷⁵) will:

  • skip the “< 1e75” scaling block (since B/1e75 ≠ 0),
  • overflow if you try to multiply by 10 (B > floor(MAX_INT256/10)),
  • remain unchanged with |B| < 1e76,
  • and therefore hit the scale = 1e75 fallback.

Action items:

• Retain (or document) the 1e75 fallback—it's needed for non-zero B in [1e75, 1e76).
• Add regression tests for coefficients just above 5.789e75 and below 1e76 to cover this path.

Likely an incorrect or invalid review comment.


491-494: Manual inversion in log10 negative branch is intentionally tied to the log‐table scale

The div(1e37, -37, …) in the negative branch of log10 is deliberate: by using 1e37/–37 you guarantee the inverted coefficient stays within [1e37…1e38) so that, after dividing by the table scale (1e34), your lookup index (x1Coefficient – 1000) falls in [0…9000). Switching to inv() (1e76/–76) would push that coefficient out of range and break the table lookup.

Please add an inline comment above the negative branch to document that this constant choice is tied to the log tables and must not be replaced with a generic inv() call:

     // This is a negative log. i.e. log(x) where 0 < x < 1.
     // log(x) = -log(1/x)
-    else {
-        (signedCoefficient, exponent) = div(1e37, -37, signedCoefficient, exponent);
+    else {
+        // Invert using 1e37/–37 so the result stays in [1e37…1e38) for the log10 tables (scale=1e34).
+        (signedCoefficient, exponent) = div(1e37, -37, signedCoefficient, exponent);
         (signedCoefficient, exponent) = log10(tablesDataContract, signedCoefficient, exponent);
         return minus(signedCoefficient, exponent);
     }

Likely an incorrect or invalid review comment.

Comment thread src/error/ErrDecimalFloat.sol Outdated
Comment thread src/lib/implementation/LibDecimalFloatImplementation.sol
Comment thread src/lib/implementation/LibDecimalFloatImplementation.sol Outdated
Comment thread test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol
Comment thread test/src/lib/LibDecimalFloat.mixed.t.sol
Comment thread test/src/lib/LibDecimalFloat.pow.t.sol
Comment thread test/src/lib/LibDecimalFloat.pow.t.sol
Comment thread test/src/lib/LibDecimalFloat.pow10.t.sol
thedavidmeister and others added 3 commits August 16, 2025 16:38
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@thedavidmeister thedavidmeister self-assigned this Aug 16, 2025
…language/rain.math.float into 2025-08-15-minimize-denominator-div
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 (1)
src/lib/implementation/LibDecimalFloatImplementation.sol (1)

202-223: Duplicate abs-conversion blocks; factor into a helper for clarity and reuse.

This exact logic appears twice; extract to a single internal helper for maintainability and fewer footguns around int256.min.

Apply this diff to the duplicated blocks:

-            uint256 signedCoefficientAAbs;
-            if (signedCoefficientA < 0) {
-                if (signedCoefficientA == type(int256).min) {
-                    signedCoefficientAAbs = uint256(type(int256).max) + 1;
-                } else {
-                    signedCoefficientAAbs = uint256(-signedCoefficientA);
-                }
-            } else {
-                signedCoefficientAAbs = uint256(signedCoefficientA);
-            }
-            uint256 signedCoefficientBAbs;
-            if (signedCoefficientB < 0) {
-                if (signedCoefficientB == type(int256).min) {
-                    signedCoefficientBAbs = uint256(type(int256).max) + 1;
-                } else {
-                    signedCoefficientBAbs = uint256(-signedCoefficientB);
-                }
-            } else {
-                signedCoefficientBAbs = uint256(signedCoefficientB);
-            }
+            uint256 signedCoefficientAAbs = _absToUint256(signedCoefficientA);
+            uint256 signedCoefficientBAbs = _absToUint256(signedCoefficientB);

And add this helper inside the same library (outside the selected range):

function _absToUint256(int256 x) internal pure returns (uint256) {
    unchecked {
        if (x >= 0) return uint256(x);
        return x == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-x);
    }
}
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9c7439b and 141c246.

📒 Files selected for processing (1)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: git-clean
🔇 Additional comments (3)
src/lib/implementation/LibDecimalFloatImplementation.sol (3)

4-4: Importing MulDivOverflow is appropriate and scoped correctly.

The new error is wired for mulDiv overflow/invalid-denominator signaling. No issues here.


246-331: mulDiv implementation otherwise matches the well-known 512-bit pattern.

Beyond the denominator-0 fast-path issue above, the rest of the implementation (remainder subtraction, power-of-two factoring, modular inverse via Newton-Raphson) looks correct and gas-competitive.


501-504: inv now reuses div at the 1e76 scale — good simplification.

Centralizing reciprocal behavior through div is cleaner and keeps invariant logic in one place.

Comment thread src/lib/implementation/LibDecimalFloatImplementation.sol Outdated
Comment thread src/lib/implementation/LibDecimalFloatImplementation.sol Outdated
Comment thread src/lib/implementation/LibDecimalFloatImplementation.sol Outdated
Comment thread src/lib/implementation/LibDecimalFloatImplementation.sol
thedavidmeister and others added 2 commits August 18, 2025 14:48
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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: 1

♻️ Duplicate comments (4)
src/lib/implementation/LibDecimalFloatImplementation.sol (4)

64-68: MINIMIZED_ZERO_ are currently unused; either remove or wire into zero fast-paths.*
These constants aren’t referenced in div/maximize/normalize. Prefer wiring them into div’s 0/B fast-path (see next comment) to canonicalize zero and avoid drift, or remove them to keep the surface clean.


197-201: Add a fast-path for zero dividend to preserve canonical zero and avoid unnecessary work.
As-is, 0/B flows through maximize(scale) and returns 0 with a shifted exponent, deviating from the spec text (“adjust remains 0 when dividend is 0”), and does unnecessary mulDiv. Short-circuit right after maximizing A.

Apply this diff:

             // Move both coefficients into the e75/e76 range, so that the result
             // of division will not cause a mulDiv overflow.
             (signedCoefficientA, exponentA) = maximize(signedCoefficientA, exponentA);
+            // Fast-path: 0 / B = 0 (canonical minimized zero).
+            if (signedCoefficientA == 0) {
+                return (MINIMIZED_ZERO_SIGNED_COEFFICIENT, MINIMIZED_ZERO_EXPONENT);
+            }
             (signedCoefficientB, exponentB) = maximize(signedCoefficientB, exponentB);

202-223: DRY up the absolute-value conversion for A/B into a tiny helper.
Duplicate blocks for int256→uint256 absolute conversion (with int256.min handling). Factor to a helper for clarity and smaller bytecode.

Apply this diff:

-            // mulDiv only works with unsigned integers, so get the absolute
-            // values of the coefficients.
-            uint256 signedCoefficientAAbs;
-            if (signedCoefficientA < 0) {
-                if (signedCoefficientA == type(int256).min) {
-                    signedCoefficientAAbs = uint256(type(int256).max) + 1;
-                } else {
-                    signedCoefficientAAbs = uint256(-signedCoefficientA);
-                }
-            } else {
-                signedCoefficientAAbs = uint256(signedCoefficientA);
-            }
-            uint256 signedCoefficientBAbs;
-            if (signedCoefficientB < 0) {
-                if (signedCoefficientB == type(int256).min) {
-                    signedCoefficientBAbs = uint256(type(int256).max) + 1;
-                } else {
-                    signedCoefficientBAbs = uint256(-signedCoefficientB);
-                }
-            } else {
-                signedCoefficientBAbs = uint256(signedCoefficientB);
-            }
+            // mulDiv only works with unsigned integers, so get the absolute values of the coefficients.
+            uint256 signedCoefficientAAbs = _absToUint256(signedCoefficientA);
+            uint256 signedCoefficientBAbs = _absToUint256(signedCoefficientB);

Add this helper inside the same library (outside the selected range):

function _absToUint256(int256 x) internal pure returns (uint256) {
    unchecked {
        if (x >= 0) return uint256(x);
        // Handle int256.min explicitly.
        return x == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-x);
    }
}

260-266: Fast-path can divide by zero; add explicit denominator==0 guard before returning.
When prod1==0, current code performs prod0/denominator without checking denominator. If x*y fits in 256 bits and denominator==0, this takes a bare div-by-zero instead of reverting with MulDivOverflow as intended.

Apply this diff:

-        // Handle non-overflow cases, 256 by 256 division.
-        if (prod1 == 0) {
-            unchecked {
-                return prod0 / denominator;
-            }
-        }
+        // Denominator must be non-zero to avoid div-by-zero in the fast path.
+        if (denominator == 0) {
+            revert MulDivOverflow(x, y, denominator);
+        }
+        // Handle non-overflow cases, 256 by 256 division.
+        if (prod1 == 0) {
+            unchecked {
+                return prod0 / denominator;
+            }
+        }
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 141c246 and 5fe8831.

📒 Files selected for processing (1)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
🔇 Additional comments (3)
src/lib/implementation/LibDecimalFloatImplementation.sol (3)

4-4: Importing MulDivOverflow looks correct for signaling mulDiv errors.
Using a dedicated error here is appropriate and keeps div overflow/denominator issues well-scoped.


246-330: mulDiv core implementation otherwise looks standard and solid.
With the explicit denominator==0 check (see prior comment), the implementation aligns with established patterns and error signaling.


501-504: LGTM: inv delegates to div with 1e76 scaling.
This keeps behavior consistent with the updated div scaling. Ensure tests cover extremal ranges and inv(0) reverting semantics.

Would you like me to add tests for:

  • div(0, eA, B, eB) returns canonical zero (0e0),
  • inv(0) reverts with MulDivOverflow,
  • large/small operand pairs exercising both 1e76 and 1e75 branches?

Comment thread src/lib/implementation/LibDecimalFloatImplementation.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: 1

♻️ Duplicate comments (4)
src/lib/implementation/LibDecimalFloatImplementation.sol (4)

192-196: Add 0-dividend fast-path after maximizing A to avoid unnecessary work and exponent drift.
Returning immediately for 0 / B avoids scaling B and the mulDiv call. This was previously requested and still applicable.

Apply this diff:

             (signedCoefficientA, exponentA) = maximize(signedCoefficientA, exponentA);
+            // Fast-path: 0 / B = 0 (return the canonical zero form).
+            if (signedCoefficientA == 0) {
+                return (MAXIMIZED_ZERO_SIGNED_COEFFICIENT, MAXIMIZED_ZERO_EXPONENT);
+            }
             (signedCoefficientB, exponentB) = maximize(signedCoefficientB, exponentB);

197-218: Duplicate abs-conversion blocks; factor into a small helper for clarity and gas.
The two blocks are identical and handle int256.min edge case. Factor them out to reduce bytecode and improve readability.

Apply this diff:

-            uint256 signedCoefficientAAbs;
-            if (signedCoefficientA < 0) {
-                if (signedCoefficientA == type(int256).min) {
-                    signedCoefficientAAbs = uint256(type(int256).max) + 1;
-                } else {
-                    signedCoefficientAAbs = uint256(-signedCoefficientA);
-                }
-            } else {
-                signedCoefficientAAbs = uint256(signedCoefficientA);
-            }
-            uint256 signedCoefficientBAbs;
-            if (signedCoefficientB < 0) {
-                if (signedCoefficientB == type(int256).min) {
-                    signedCoefficientBAbs = uint256(type(int256).max) + 1;
-                } else {
-                    signedCoefficientBAbs = uint256(-signedCoefficientB);
-                }
-            } else {
-                signedCoefficientBAbs = uint256(signedCoefficientB);
-            }
+            uint256 signedCoefficientAAbs = _absToUint256(signedCoefficientA);
+            uint256 signedCoefficientBAbs = _absToUint256(signedCoefficientB);

Add this helper inside the same library (outside the selected range):

function _absToUint256(int256 x) internal pure returns (uint256) {
    unchecked {
        if (x >= 0) return uint256(x);
        return x == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-x);
    }
}

220-236: Use unsigned magnitude for scale decision; fix comment; drop signed division.
Basing the choice on signed division is unnecessary and makes the intent murkier. Compare the unsigned magnitude directly; also the comment should say “smaller than or equal to”.

Apply this diff:

-            // We are going to scale the numerator up by the largest power of ten
-            // that is smaller than the denominator. This will always overflow
-            // internally to the mulDiv during the initial multiplication, in
-            // 512 bits, but will subsequently always be reduced back down to
-            // fit in 256 bits by the division of a denominator that is larger
-            // than the scale up.
-            int256 scale = 1e76;
-            int256 adjustExponent = 76;
-            if (signedCoefficientB / scale == 0) {
-                scale = 1e75;
-                adjustExponent = 75;
-            }
-            uint256 signedCoefficientAbs = mulDiv(signedCoefficientAAbs, uint256(scale), signedCoefficientBAbs);
+            // Scale the numerator by the largest power of ten that is smaller than
+            // or equal to the denominator's magnitude. The initial 256-bit product
+            // may overflow, but the 512-bit mulDiv path handles it and the subsequent
+            // division reduces the result back to 256 bits.
+            uint256 scale = 1e76;
+            int256 adjustExponent = 76;
+            if (signedCoefficientBAbs < scale) {
+                scale = 1e75;
+                adjustExponent = 75;
+            }
+            uint256 signedCoefficientAbs = mulDiv(signedCoefficientAAbs, scale, signedCoefficientBAbs);

255-260: mulDiv fast-path can divide by zero; add explicit denominator check before returning.
If x*y fits in 256 bits (prod1 == 0) and denominator == 0, this branch reverts with a bare div-by-zero instead of the intended MulDivOverflow. Guard it explicitly.

Apply this diff:

-        // Handle non-overflow cases, 256 by 256 division.
-        if (prod1 == 0) {
-            unchecked {
-                return prod0 / denominator;
-            }
-        }
+        // Denominator must be non-zero to avoid div-by-zero in the fast path.
+        if (denominator == 0) {
+            revert MulDivOverflow(x, y, denominator);
+        }
+        // Handle non-overflow cases, 256 by 256 division.
+        if (prod1 == 0) {
+            unchecked {
+                return prod0 / denominator;
+            }
+        }
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5fe8831 and 887f4a7.

📒 Files selected for processing (1)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: git-clean
🔇 Additional comments (2)
src/lib/implementation/LibDecimalFloatImplementation.sol (2)

4-4: Importing MulDivOverflow looks correct and is used below.
The new error is properly referenced in mulDiv; no issues here.


241-243: mulDiv attribution and shape LGTM.
The implementation matches the well-known 512-bit division pattern; attribution and license note are in place.

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 (4)
src/lib/implementation/LibDecimalFloatImplementation.sol (4)

199-218: DRY the duplicate int256→uint256 absolute-value conversions.

The two blocks computing abs with int256.min handling are duplicated and hurt readability. Factor into a small helper.

-            uint256 signedCoefficientAAbs;
-            if (signedCoefficientA < 0) {
-                if (signedCoefficientA == type(int256).min) {
-                    signedCoefficientAAbs = uint256(type(int256).max) + 1;
-                } else {
-                    signedCoefficientAAbs = uint256(-signedCoefficientA);
-                }
-            } else {
-                signedCoefficientAAbs = uint256(signedCoefficientA);
-            }
-            uint256 signedCoefficientBAbs;
-            if (signedCoefficientB < 0) {
-                if (signedCoefficientB == type(int256).min) {
-                    signedCoefficientBAbs = uint256(type(int256).max) + 1;
-                } else {
-                    signedCoefficientBAbs = uint256(-signedCoefficientB);
-                }
-            } else {
-                signedCoefficientBAbs = uint256(signedCoefficientB);
-            }
+            uint256 signedCoefficientAAbs = _absToUint256(signedCoefficientA);
+            uint256 signedCoefficientBAbs = _absToUint256(signedCoefficientB);

Add this helper inside the same library (outside the selected range):

function _absToUint256(int256 x) internal pure returns (uint256) {
    unchecked {
        if (x >= 0) return uint256(x);
        // Handle int256.min explicitly.
        return x == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-x);
    }
}

255-266: Critical: mulDiv fast path can divide by zero; add explicit denominator check before returning.

When prod1 == 0, the function returns prod0 / denominator without checking denominator. If x*y fits in 256 bits and denominator == 0, this triggers a raw division-by-zero revert instead of your intended MulDivOverflow error, and also breaks inv(0) semantics.

Apply this diff:

-        // Handle non-overflow cases, 256 by 256 division.
-        if (prod1 == 0) {
-            unchecked {
-                return prod0 / denominator;
-            }
-        }
+        // Denominator must be non-zero to avoid a raw div-by-zero in the fast path.
+        if (denominator == 0) {
+            revert MulDivOverflow(x, y, denominator);
+        }
+        // Micro-optimization: if either multiplicand is zero, the result is zero.
+        if (x == 0 || y == 0) {
+            return 0;
+        }
+        // Handle non-overflow cases, 256 by 256 division.
+        if (prod1 == 0) {
+            unchecked {
+                return prod0 / denominator;
+            }
+        }

192-199: Add zero-dividend fast path in div to preserve spec (adjust=0) and avoid unnecessary work.

Currently, 0/B flows through abs/scale/mulDiv and returns a zero coefficient with a shifted exponent (exponentA - exponentB - adjustExponent), which contradicts the referenced algorithm (“adjust remains 0 when dividend is 0”) and does extra work.

Apply this diff right after maximizing A:

             (signedCoefficientA, exponentA) = maximize(signedCoefficientA, exponentA);
+            // Fast-path: 0 / B = 0. Preserve canonical zero and avoid unnecessary work.
+            if (signedCoefficientA == 0) {
+                return (NORMALIZED_ZERO_SIGNED_COEFFICIENT, NORMALIZED_ZERO_EXPONENT);
+            }
             (signedCoefficientB, exponentB) = maximize(signedCoefficientB, exponentB);

220-226: Clarify the scale-selection comment; match behavior and avoid “always overflow” claim.

Wording nits:

  • It’s “smaller than or equal to” the denominator’s magnitude (since scale can equal |B| when |B|≈1e75).
  • The initial 512-bit multiplication may overflow, not “will always”.
-            // We are going to scale the numerator up by the largest power of ten
-            // that is smaller than the denominator. This will always overflow
-            // internally to the mulDiv during the initial multiplication, in
+            // We are going to scale the numerator up by the largest power of ten
+            // that is smaller than or equal to the denominator's magnitude. This may overflow
+            // internally in mulDiv during the initial multiplication, in
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 887f4a7 and d0c8201.

📒 Files selected for processing (2)
  • .gas-snapshot (2 hunks)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-11T14:30:48.562Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/LibDecimalFloat.ceil.t.sol:43-50
Timestamp: 2025-08-11T14:30:48.562Z
Learning: When reviewing Solidity test files using Forge, verify the actual mutability of helper functions like `bound()` before suggesting changes to function mutability specifiers, as Forge implements many test utilities as pure functions.

Applied to files:

  • src/lib/implementation/LibDecimalFloatImplementation.sol
🔇 Additional comments (2)
src/lib/implementation/LibDecimalFloatImplementation.sol (1)

4-4: Importing MulDivOverflow is appropriate and consistent with the new mulDiv helper.

Good preparatory change for surfacing a consistent revert reason from mulDiv.

.gas-snapshot (1)

1-305: Gas snapshot updates look consistent with the div/inv refactor; watch the outliers.

Notable increases around division-heavy tests (e.g., testUnnormalizedThreesDiv0 μ: ~24.5M) align with the new mulDiv path and scale/maximize work. If any of these deltas are unexpected, consider:

  • Adding a zero-dividend short-circuit in div (suggested above) to shave gas in common 0/B cases.
  • Early-return micro-opts in mulDiv for x==0||y==0 (also suggested) to reduce overhead in degenerate paths.

Otherwise, snapshot changes LGTM.

Comment thread src/lib/implementation/LibDecimalFloatImplementation.sol Outdated
Comment thread src/lib/implementation/LibDecimalFloatImplementation.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: 1

♻️ Duplicate comments (4)
src/lib/implementation/LibDecimalFloatImplementation.sol (4)

192-196: Add zero-dividend fast-path before maximizing B to avoid unnecessary work.

Currently B is maximized even when A becomes 0 after maximizing, which is wasted gas. Short-circuit right after maximizing A, then only maximize B if needed.

Apply this reordering:

-            (signedCoefficientA, exponentA) = maximize(signedCoefficientA, exponentA);
-            (signedCoefficientB, exponentB) = maximize(signedCoefficientB, exponentB);
+            (signedCoefficientA, exponentA) = maximize(signedCoefficientA, exponentA);
+            // Fast-path: 0 / B = 0 (return the canonical zero form) before doing any more work.
+            if (signedCoefficientA == 0) {
+                return (MAXIMIZED_ZERO_SIGNED_COEFFICIENT, MAXIMIZED_ZERO_EXPONENT);
+            }
+            (signedCoefficientB, exponentB) = maximize(signedCoefficientB, exponentB);

197-211: Duplicate abs conversion logic; consider a tiny helper (optional).

Two near-identical blocks convert int256 to absolute uint256 with int256.min handling. A small internal helper improves readability with negligible overhead.

Minimal in-place refactor:

-            uint256 signedCoefficientAAbs;
-            if (signedCoefficientA > 0) {
-                signedCoefficientAAbs = uint256(signedCoefficientA);
-            } else if (signedCoefficientA < 0) {
-                if (signedCoefficientA == type(int256).min) {
-                    signedCoefficientAAbs = uint256(type(int256).max) + 1;
-                } else {
-                    signedCoefficientAAbs = uint256(-signedCoefficientA);
-                }
-            } else {
-                return (MAXIMIZED_ZERO_SIGNED_COEFFICIENT, MAXIMIZED_ZERO_EXPONENT);
-            }
+            uint256 signedCoefficientAAbs;
+            if (signedCoefficientA > 0) {
+                signedCoefficientAAbs = uint256(signedCoefficientA);
+            } else if (signedCoefficientA < 0) {
+                signedCoefficientAAbs = signedCoefficientA == type(int256).min
+                    ? uint256(type(int256).max) + 1
+                    : uint256(-signedCoefficientA);
+            } else {
+                return (MAXIMIZED_ZERO_SIGNED_COEFFICIENT, MAXIMIZED_ZERO_EXPONENT);
+            }

Add this helper outside the selected range (optional):

// Place anywhere inside the library.
function _absToUint256(int256 x) internal pure returns (uint256) {
    unchecked {
        if (x >= 0) return uint256(x);
        return x == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-x);
    }
}

Note: A prior learning indicates a preference to keep such logic inline for gas; treat this as optional.


234-239: Guard exponent arithmetic from wrapping.

exponent = exponentA - exponentB - adjustExponent can wrap unchecked at extreme inputs. Add a cheap round-trip validation and revert with ExponentOverflow on failure, mirroring mul()’s robustness.

             int256 exponent = exponentA - exponentB - adjustExponent;
+            // Validate two's-complement round-trip to catch wraparound.
+            unchecked {
+                if (exponent + exponentB + adjustExponent != exponentA) {
+                    revert ExponentOverflow(signedCoefficient, exponent);
+                }
+            }

243-330: mulDiv fast-path can divide by zero; add explicit denominator check before early return.

If prod1 == 0 and denominator == 0, current code hits a raw div-by-zero instead of reverting with MulDivOverflow, breaking error consistency.

Apply this fix:

         // Handle non-overflow cases, 256 by 256 division.
-        if (prod1 == 0) {
+        // Ensure consistent revert semantics for denominator == 0 as well.
+        if (denominator == 0) {
+            revert MulDivOverflow(x, y, denominator);
+        }
+        if (prod1 == 0) {
             unchecked {
                 return prod0 / denominator;
             }
         }
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d0c8201 and 194e3df.

📒 Files selected for processing (2)
  • .gas-snapshot (2 hunks)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-11T14:30:48.562Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/LibDecimalFloat.ceil.t.sol:43-50
Timestamp: 2025-08-11T14:30:48.562Z
Learning: When reviewing Solidity test files using Forge, verify the actual mutability of helper functions like `bound()` before suggesting changes to function mutability specifiers, as Forge implements many test utilities as pure functions.

Applied to files:

  • src/lib/implementation/LibDecimalFloatImplementation.sol
📚 Learning: 2025-08-14T16:32:05.932Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#99
File: src/lib/implementation/LibDecimalFloatImplementation.sol:309-325
Timestamp: 2025-08-14T16:32:05.932Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister prefers to keep assembly-based overflow checks inline for gas optimization rather than extracting them into helper functions, even when it results in code duplication.

Applied to files:

  • src/lib/implementation/LibDecimalFloatImplementation.sol
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: git-clean
🔇 Additional comments (5)
src/lib/implementation/LibDecimalFloatImplementation.sol (3)

4-4: Importing MulDivOverflow is correct and consistent with usage.

Matches the new error signaling in mulDiv/div paths. No concerns.


211-220: LGTM: Symmetric absolute-magnitude handling for divisor.

The int256.min case is correctly handled; unsigned magnitude is ready for the scaling logic.


501-504: inv delegation to div(1e76,-76,…) is sound; please ensure revert-type coverage for inv(0).

The new path will bubble up MulDivOverflow on zero input via mulDiv; add/confirm a test asserting the exact revert type for inv(0).

I can draft a focused Forge test that asserts inv(0) reverts with MulDivOverflow and exercises both scale branches around the 1e75/1e76 threshold. Want me to open a PR with those tests?

.gas-snapshot (2)

147-155: Division gas shifts look consistent with full-precision mulDiv scaling.

The updated μ/~ across div tests align with the new scaling and 512/256 division path; snapshot changes are reasonable.


156-156: Runs reduced to 100 for testUnnormalizedThreesDiv0; confirm this is intentional.

Significant gas per-run is understandable, but please confirm the reduced fuzz count is deliberate and that both 1e75 and 1e76 scale branches remain covered elsewhere.

If helpful, I can add narrowly scoped tests that deterministically hit each branch and assert revert types for zero-divisor cases.

Comment thread src/lib/implementation/LibDecimalFloatImplementation.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: 1

♻️ Duplicate comments (2)
src/lib/implementation/LibDecimalFloatImplementation.sol (2)

201-224: Duplicate abs-conversion logic; consider a tiny helper (acknowledging prior preference to inline)

Both A and B convert int256 to uint256 with the int256.min edge case. A small internal helper would DRY this up. Not blocking given your gas/inline preferences noted previously.

If you ever decide to factor it, here’s a drop-in helper (outside the selected range):

function _absToUint256(int256 x) internal pure returns (uint256) {
    unchecked {
        if (x >= 0) return uint256(x);
        return x == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-x);
    }
}

Then:

  • signedCoefficientAAbs = _absToUint256(signedCoefficientA);
  • signedCoefficientBAbs = _absToUint256(signedCoefficientB);

226-235: Comment nit: clarify “smaller than” → “smaller than or equal to”

The code keeps scale at 1e76 when |B| >= 1e76 and uses 1e75 otherwise, so the scale is ≤ |B|. Update the comment to reflect this precisely.

Apply this wording tweak:

-            // We are going to scale the numerator up by the largest power of ten
-            // that is smaller than the denominator. This will always overflow
+            // We scale the numerator by the largest power of ten that is smaller than
+            // or equal to the denominator's magnitude. This will always overflow
             // internally to the mulDiv during the initial multiplication, in
             // 512 bits, but will subsequently always be reduced back down to
             // fit in 256 bits by the division of a denominator that is larger
             // than the scale up.
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 194e3df and a849575.

📒 Files selected for processing (2)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (3 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.inv.t.sol (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-11T14:30:48.562Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/LibDecimalFloat.ceil.t.sol:43-50
Timestamp: 2025-08-11T14:30:48.562Z
Learning: When reviewing Solidity test files using Forge, verify the actual mutability of helper functions like `bound()` before suggesting changes to function mutability specifiers, as Forge implements many test utilities as pure functions.

Applied to files:

  • src/lib/implementation/LibDecimalFloatImplementation.sol
📚 Learning: 2025-08-14T16:32:05.932Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#99
File: src/lib/implementation/LibDecimalFloatImplementation.sol:309-325
Timestamp: 2025-08-14T16:32:05.932Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister prefers to keep assembly-based overflow checks inline for gas optimization rather than extracting them into helper functions, even when it results in code duplication.

Applied to files:

  • src/lib/implementation/LibDecimalFloatImplementation.sol
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
🔇 Additional comments (8)
src/lib/implementation/LibDecimalFloatImplementation.sol (5)

4-4: Importing MulDivOverflow here is appropriate

This aligns the implementation with the new error type used by mulDiv/div and keeps selectors centralized.


191-200: Solid setup to keep division within mulDiv-safe ranges

Initializing scale/adjustExponent and maximizing both operands up-front reads well and matches the new approach.


236-240: Sign computation via XOR is correct and gas-friendly

Good use of XOR on signs and a single cast of the magnitude.


242-245: Good: exponent arithmetic is checked (outside unchecked)

Keeping exponentA - exponentB - adjustExponent in a checked context prevents silent wraparound.


506-509: inv simplified via div(1e76, -76, ...) — clearer and ensures consistent scaling

This cleanly routes through the new div path; inv(0) semantics are now testable and consistent with MulDivOverflow.

Consider adding/confirming tests around the 1e75/1e76 threshold to exercise both scale branches in div/inv (|B| just below 1e76 and exactly 1e76), including negative coefficient cases, to cement edge behaviors.

test/src/lib/implementation/LibDecimalFloatImplementation.inv.t.sol (3)

9-11: Importing MulDivOverflow alongside EXPONENT bounds keeps the test self-contained

This makes the revert-assert explicit and stable against selector drift.


14-17: Wrapper is a minimal and effective harness for testing the internal inv

Using an external method to surface the revert is appropriate here.


43-46: inv(0) revert assertion matches the intended mulDiv path

Asserting MulDivOverflow(1e76, 1e75, 0) aligns with the div scaling logic when the divisor is zero.

To round out coverage, consider adding:

  • A direct div(..., 0) test to confirm the same revert pattern via div, and
  • Boundary cases around |B| = 1e76 and |B| = 1e76 - 1 (and sign variants) to exercise both scale branches.

Comment thread src/lib/implementation/LibDecimalFloatImplementation.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: 2

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a849575 and 2f8ec84.

📒 Files selected for processing (1)
  • crates/float/src/lib.rs (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: git-clean
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)

Comment thread crates/float/src/lib.rs Outdated
Comment thread crates/float/src/lib.rs Outdated
Comment thread crates/float/src/lib.rs Outdated
Comment thread crates/float/src/lib.rs Outdated
Comment thread crates/float/src/lib.rs Outdated
@thedavidmeister thedavidmeister merged commit 5310bd5 into main Aug 20, 2025
7 of 8 checks passed
@github-actions
Copy link
Copy Markdown

@coderabbitai assess this PR size classification for the totality of the PR with the following criterias and report it in your comment:

S/M/L PR Classification Guidelines:

This guide helps classify merged pull requests by effort and complexity rather than just line count. The goal is to assess the difficulty and scope of changes after they have been completed.

Small (S)

Characteristics:

  • Simple bug fixes, typos, or minor refactoring
  • Single-purpose changes affecting 1-2 files
  • Documentation updates
  • Configuration tweaks
  • Changes that require minimal context to review

Review Effort: Would have taken 5-10 minutes

Examples:

  • Fix typo in variable name
  • Update README with new instructions
  • Adjust configuration values
  • Simple one-line bug fixes
  • Import statement cleanup

Medium (M)

Characteristics:

  • Feature additions or enhancements
  • Refactoring that touches multiple files but maintains existing behavior
  • Breaking changes with backward compatibility
  • Changes requiring some domain knowledge to review

Review Effort: Would have taken 15-30 minutes

Examples:

  • Add new feature or component
  • Refactor common utility functions
  • Update dependencies with minor breaking changes
  • Add new component with tests
  • Performance optimizations
  • More complex bug fixes

Large (L)

Characteristics:

  • Major feature implementations
  • Breaking changes or API redesigns
  • Complex refactoring across multiple modules
  • New architectural patterns or significant design changes
  • Changes requiring deep context and multiple review rounds

Review Effort: Would have taken 45+ minutes

Examples:

  • Complete new feature with frontend/backend changes
  • Protocol upgrades or breaking changes
  • Major architectural refactoring
  • Framework or technology upgrades

Additional Factors to Consider

When deciding between sizes, also consider:

  • Test coverage impact: More comprehensive test changes lean toward larger classification
  • Risk level: Changes to critical systems bump up a size category
  • Team familiarity: Novel patterns or technologies increase complexity

Notes:

  • the assessment must be for the totality of the PR, that means comparing the base branch to the last commit of the PR
  • the assessment output must be exactly one of: S, M or L (single-line comment) in format of: SIZE={S/M/L}
  • do not include any additional text, only the size classification
  • your assessment comment must not include tips or additional sections
  • do NOT tag me or anyone else on your comment

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 20, 2025

Tip

For best results, initiate chat on the files or code changes.

SIZE=L

This was referenced Aug 20, 2025
This was referenced Aug 27, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Sep 9, 2025
7 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.

2 participants