Skip to content

2025 09 03 div zero#125

Merged
thedavidmeister merged 2 commits intomainfrom
2025-09-03-div-zero
Sep 3, 2025
Merged

2025 09 03 div zero#125
thedavidmeister merged 2 commits intomainfrom
2025-09-03-div-zero

Conversation

@thedavidmeister
Copy link
Copy Markdown
Contributor

@thedavidmeister thedavidmeister commented Sep 3, 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

  • Bug Fixes

    • Updated decimal division behavior: 0 divided by 0 now reverts with a division error; 0 divided by a non-zero value remains zero.
  • Tests

    • Added coverage for zero-division and overflow scenarios in decimal division, including an external wrapper for direct invocation in tests.
  • Chores

    • Refreshed gas snapshot metrics to reflect latest test runs.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 3, 2025

Walkthrough

Tightens zero-division short-circuit in LibDecimalFloatImplementation.div, causing 0/0 to enter full division path. Adds tests and an external wrapper to validate division-by-zero and MulDivOverflow behavior. Updates .gas-snapshot metrics across many tests; no public API changes to production contracts.

Changes

Cohort / File(s) Summary
Gas snapshot updates
\.gas-snapshot
Refreshes recorded gas/metrics for numerous DecimalFloat tests; values (runs, μ, ~) adjusted; no test identities changed.
Division logic change
src/lib/implementation/LibDecimalFloatImplementation.sol
Modifies div zero-case: short-circuit now only when signedCoefficientA == 0 && signedCoefficientB != 0; 0/0 proceeds into main division path (may revert).
Division tests and wrapper
test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol
Adds external divExternal(...) wrapper; introduces testDivZero(...) covering 0/0 and overflow cases; imports stdError and MulDivOverflow for revert assertions.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant T as LibDecimalFloatImplementationDivTest
  participant L as LibDecimalFloatImplementation.div

  Note over T: testDivZero(signedCoefficient, exponent)
  T->>T: maximize(signedCoefficient, exponent)
  T->>L: divExternal(a= signedCoefficient,<br/>ea= exponent,<br/>b=0, eb=0)

  alt a == 0 && b != 0
    Note over L: Short-circuit return zero (unchanged path)
    L-->>T: (0, adjustedExponent)
  else a == 0 && b == 0  (new path)
    Note over L: Enters main division flow
    opt May compute mulDiv(..., denom=0)
      L-->>T: revert stdError.divisionError
    end
    opt Alternative scaling path
      L-->>T: revert MulDivOverflow(...)
    end
  else a != 0
    Note over L: Normal division path
    L-->>T: (quotient, exponent)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • 2025 08 16 no normal mul #107 — Also rewrites LibDecimalFloatImplementation.div to a 512-bit scaled-numerator approach; overlaps in division flow changes.
  • 2025 08 25 div by one #111 — Adjusts div exponent/overflow handling and adds div-by-one tests; intersects with div logic and testing of edge cases.
  • full precision div #105 — Changes div behavior and MulDiv/MulDivOverflow handling with related tests; closely aligned with zero/overflow paths.

Suggested reviewers

  • hardyjosh
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 2025-09-03-div-zero

🪧 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 or @coderabbit 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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/lib/implementation/LibDecimalFloatImplementation.sol (2)

228-271: Unify divide-by-zero behavior with an explicit, deterministic revert

Right now, divisor==0 surfaces as either a builtin divisionError (0/0) or MulDivOverflow (x≠0/0) via mulDiv, which is observable and brittle. Consider making this explicit and consistent at the top of div:

  • If signedCoefficientB == 0:
    • If signedCoefficientA == 0: revert DivisionUndefined();
    • Else: revert DivisionByZero(signedCoefficientA, exponentA);
  • Else if signedCoefficientA == 0: return maximized zero.

This matches the decimal spec’s “Division undefined/by zero” cases and avoids relying on mulDiv side effects.

If you adopt this, tests expecting the current two different errors will need to be updated.

Proposed patch within this function:

-        if (signedCoefficientA == 0 && signedCoefficientB != 0) {
+        if (signedCoefficientB == 0) {
+            // Deterministic div-by-zero semantics.
+            if (signedCoefficientA == 0) {
+                revert DivisionUndefined();
+            }
+            revert DivisionByZero(signedCoefficientA, exponentA);
+        } else if (signedCoefficientA == 0) {
             signedCoefficient = MAXIMIZED_ZERO_SIGNED_COEFFICIENT;
             exponent = MAXIMIZED_ZERO_EXPONENT;
         } else {

Additional (outside this hunk): define the two errors in ErrDecimalFloat.sol and import them here alongside the existing errors.


245-254: Comment nit: reflect actual condition (“≤” instead of “<”)

The code keeps scale at 1e76 when signedCoefficientBAbs == 1e76, so the scale can be equal to the denominator. Tweak the comment to avoid confusion.

-            // 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 are going to scale the numerator up by the largest power of ten
+            // that is smaller than or equal to the denominator. This will always overflow
📜 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 af48be6 and 51fdbdb.

📒 Files selected for processing (3)
  • .gas-snapshot (2 hunks)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (1 hunks)
  • test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol (2 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 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.
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#107
File: test/lib/LibDecimalFloatSlow.sol:37-45
Timestamp: 2025-08-21T18:03:40.347Z
Learning: In test/lib/LibDecimalFloatSlow.sol, the "slow" implementation is intentionally different from the production implementation to serve as an independent reference for fuzzing tests. The goal is to have two different approaches (expensive loops vs optimized jumps) that produce equivalent results, not identical implementations.
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: test/src/lib/LibDecimalFloat.decimal.t.sol:122-126
Timestamp: 2025-08-29T14:54:24.240Z
Learning: In test/src/lib/LibDecimalFloat.decimal.t.sol, the fromFixedDecimalLossy function converts uint256(type(int256).max) losslessly because the boundary check is `value > uint256(type(int256).max)`, not `>=`. Values exactly equal to type(int256).max still take the lossless conversion path.
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: test/src/lib/LibDecimalFloat.decimal.t.sol:122-126
Timestamp: 2025-08-29T14:54:24.240Z
Learning: In test/src/lib/LibDecimalFloat.decimal.t.sol, the fromFixedDecimalLossy function converts uint256(type(int256).max) losslessly because the boundary check is `value > uint256(type(int256).max)`, not `>=`. Values exactly equal to type(int256).max still take the lossless conversion path.
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: src/lib/implementation/LibDecimalFloatImplementation.sol:593-601
Timestamp: 2025-08-29T10:38:26.353Z
Learning: The maximize function in LibDecimalFloatImplementation.sol produces exact results for simple integer values like 1. maximize(1, 0) yields exactly (1e76, -76) with no precision loss, and the log10 special case for signedCoefficient == 1e76 correctly handles this.
📚 Learning: 2025-08-21T18:03:40.347Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#107
File: test/lib/LibDecimalFloatSlow.sol:37-45
Timestamp: 2025-08-21T18:03:40.347Z
Learning: In test/lib/LibDecimalFloatSlow.sol, the "slow" implementation is intentionally different from the production implementation to serve as an independent reference for fuzzing tests. The goal is to have two different approaches (expensive loops vs optimized jumps) that produce equivalent results, not identical implementations.

Applied to files:

  • src/lib/implementation/LibDecimalFloatImplementation.sol
  • test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol
  • .gas-snapshot
📚 Learning: 2025-08-29T14:54:24.240Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: test/src/lib/LibDecimalFloat.decimal.t.sol:122-126
Timestamp: 2025-08-29T14:54:24.240Z
Learning: In test/src/lib/LibDecimalFloat.decimal.t.sol, the fromFixedDecimalLossy function converts uint256(type(int256).max) losslessly because the boundary check is `value > uint256(type(int256).max)`, not `>=`. Values exactly equal to type(int256).max still take the lossless conversion path.

Applied to files:

  • src/lib/implementation/LibDecimalFloatImplementation.sol
  • test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol
  • .gas-snapshot
📚 Learning: 2025-08-29T10:38:26.353Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: src/lib/implementation/LibDecimalFloatImplementation.sol:593-601
Timestamp: 2025-08-29T10:38:26.353Z
Learning: The maximize function in LibDecimalFloatImplementation.sol produces exact results for simple integer values like 1. maximize(1, 0) yields exactly (1e76, -76) with no precision loss, and the log10 special case for signedCoefficient == 1e76 correctly handles this.

Applied to files:

  • src/lib/implementation/LibDecimalFloatImplementation.sol
  • test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol
📚 Learning: 2025-08-18T13:52:43.369Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#105
File: src/lib/implementation/LibDecimalFloatImplementation.sol:248-335
Timestamp: 2025-08-18T13:52:43.369Z
Learning: The codebase has a policy of not modifying external code, such as mulDiv implementations adopted from standard libraries like OpenZeppelin, PRB Math, and Solady.

Applied to files:

  • src/lib/implementation/LibDecimalFloatImplementation.sol
  • test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol
📚 Learning: 2025-08-29T10:38:26.353Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: src/lib/implementation/LibDecimalFloatImplementation.sol:593-601
Timestamp: 2025-08-29T10:38:26.353Z
Learning: In Solidity, int256(1) when passed through the maximize function in LibDecimalFloatImplementation.sol produces exactly (1e76, -76), not an approximation. This means the special case for signedCoefficient == 1e76 in log10 correctly handles powers of 10 like log10(1).

Applied to files:

  • src/lib/implementation/LibDecimalFloatImplementation.sol
  • test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol
  • .gas-snapshot
📚 Learning: 2025-08-29T14:58:50.500Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: src/lib/implementation/LibDecimalFloatImplementation.sol:896-899
Timestamp: 2025-08-29T14:58:50.500Z
Learning: In unchecked Solidity blocks, arithmetic operations can overflow/underflow and wrap around, so bounds checks that seem "impossible" for normal arithmetic may actually be necessary to catch overflow edge cases. For example, in withTargetExponent function, the check `exponentDiff < 0` is needed because `targetExponent - exponent` could underflow in unchecked arithmetic.

Applied to files:

  • test/src/lib/implementation/LibDecimalFloatImplementation.div.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). (9)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: rainix (macos-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
  • GitHub Check: git-clean
🔇 Additional comments (5)
src/lib/implementation/LibDecimalFloatImplementation.sol (1)

259-263: LGTM: subtraction order reduces transient overflow risk

The branch on exponentA sign to order subtractions is a solid guard against intermediate overflow in unchecked math.

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

15-22: Good wrapper to exercise external-call revert data

Using an external pass-through simplifies expectRevert assertions for both builtin and custom errors.


37-54: Clear coverage for 0/0 vs x≠0/0 paths

Nice assertion split: divisionError for 0/0 and MulDivOverflow for x≠0/0 aligns with current behavior. If you later make div-by-zero explicit (single error), update this test accordingly.


165-168: Bounds look safe for fuzzing extreme exponents

Constraining to EXPONENT_[MIN|MAX]/2 avoids pathological maximize paths while still stressing scaling logic.

.gas-snapshot (1)

1-329: Ack: snapshot drift consistent with new div paths

Gas metrics updated as expected; no actionable concerns.

Comment thread test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol
@thedavidmeister thedavidmeister merged commit 9e4bc37 into main Sep 3, 2025
10 checks passed
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Sep 3, 2025

@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 Sep 3, 2025

Tip

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

🧩 Analysis chain

Looking at PR #125, I need to assess the size classification based on the changes made.

SIZE=M


🏁 Script executed:

#!/bin/bash
# Let me examine the actual diff to understand the full scope of changes
git log --oneline -10
git diff main --stat
git diff main --numstat

Length of output: 85


🏁 Script executed:

#!/bin/bash
# Let me examine the files that were changed according to the AI summary
echo "=== Examining LibDecimalFloatImplementation.sol changes ==="
cat "src/lib/implementation/LibDecimalFloatImplementation.sol" | grep -A 10 -B 10 "signedCoefficientA == 0"

echo -e "\n=== Examining test file changes ==="
cat "test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol" | grep -A 5 -B 5 "testDivZero\|divExternal"

echo -e "\n=== Checking file sizes and line counts ==="
wc -l "src/lib/implementation/LibDecimalFloatImplementation.sol"
wc -l "test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol"
wc -l ".gas-snapshot"

Length of output: 4251


SIZE=M


🧠 Learnings used
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.

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

1 participant