Skip to content

2025 08 30 format - #119

Merged
thedavidmeister merged 25 commits into
mainfrom
2025-08-30-format
Sep 2, 2025
Merged

2025 08 30 format#119
thedavidmeister merged 25 commits into
mainfrom
2025-08-30-format

Conversation

@thedavidmeister

@thedavidmeisterthedavidmeister commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

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

    • Formatting accepts a configurable significant‑figures limit and now emits scientific notation for very large/small magnitudes; sign, zero, integral and fractional display improved. Public formatting API updated to accept the sig‑figs parameter.
  • Bug Fixes

    • Stricter precision‑loss detection when parsing, including additional overflow checks for extreme inputs.
  • Tests

    • Expanded formatting/parsing tests, added sig‑figs unit and regression tests, and updated expected outputs for scientific notation.
  • Documentation

    • Updated comments describing off‑chain, non‑gas‑sensitive formatting behavior.

@coderabbitai

coderabbitaiBot commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds significant‑figures aware decimal formatting: introduces internal countSigFigs, changes toDecimalString to accept sigFigsLimit and select scientific vs plain formatting (scientific uses LibDecimalFloatImplementation.maximize and 10^75/10^76 scaling). Tightens parse precision-loss checks, updates DecimalFloat.format signature, and expands/adjusts tests and gas snapshot.

Changes

Cohort / File(s)Summary
Format library: decimal float formatting
src/lib/format/LibFormatDecimalFloat.sol
Added countSigFigs(int256,int256); changed toDecimalString(Float,uint256) to accept sigFigsLimit and pick scientific vs plain formatting. Scientific path calls LibDecimalFloatImplementation.maximize and uses 10^75/10^76 scaling; plain path scales coefficient, derives integral/fractional parts, trims zeros, assembles optional e exponent. Imports updated (LibDecimalFloatImplementation, Strings).
Public API: concrete wrapper
src/concrete/DecimalFloat.sol
format signature changed to format(Float a, uint256 sigFigsLimit) and now delegates to updated formatter.
Parse library: precision checks
src/lib/parse/LibParseDecimalFloat.sol
Tightened precision-loss detection: scale >= 67scale > 67 for nonzero coefficient and added verification that the rescaled integer fits in int224, emitting ParseDecimalPrecisionLoss on mismatch/overflow.
Format tests (Solidity)
test/src/lib/format/LibFormatDecimalFloat.t.sol
test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
Renamed/added tests: exhaustive countSigFigs suite, toDecimalString round‑trip/examples, renamed round‑trip test, updated expected outputs to normalized scientific notation for large magnitudes, added zero/edge cases and custom sig‑fig limits.
Parse tests (Solidity)
test/src/lib/parse/LibParseDecimalFloat.t.sol
Added regression testParseFormatterRoundTripBug0 and adjusted an existing precision-revert test input while preserving expected revert behavior.
Rust float crate tests
crates/float/src/lib.rs
Updated formatting call to include sigFigsLimit = 9 and adjusted tests to expect scientific notation; removed one obsolete test.
Gas snapshot
.gas-snapshot
Updated gas/run metrics across many tests to reflect test updates and formatter changes.

Sequence Diagram(s)

sequenceDiagram
autonumber
participant Caller
participant Formatter as LibFormatDecimalFloat
participant Impl as LibDecimalFloatImplementation
Caller->>Formatter: toDecimalString(Float, sigFigsLimit)
Formatter->>Formatter: if coef == 0 → return "0"
Formatter->>Formatter: sigFigs = countSigFigs(coef, exp)
alt sigFigs > sigFigsLimit (scientific)
Formatter->>Impl: maximize(signedCoefficient, exponent)
Impl-->>Formatter: maximizedCoef, maximizedExp
Formatter->>Formatter: choose scale (10^75 or 10^76), compute mantissa
Formatter->>Formatter: assemble sign + mantissa + "e" + displayExp
else (plain decimal)
alt exponent > 0
Formatter->>Formatter: scale coef by 10^exponent → exponent = 0
else exponent < 0
Formatter->>Formatter: set scale = 10^(-exponent)
end
Formatter->>Formatter: compute integral & fractional, pad/trim zeros
Formatter->>Formatter: assemble sign + integral + fractional
end
Formatter-->>Caller: formatted string
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • ceil #96 — changes to LibDecimalFloatImplementation.maximize relied on by the new scientific formatting path.
  • 2025 08 16 no normal mul #107 — further modifications to LibDecimalFloatImplementation/maximize that affect scaling/normalization semantics used here.
  • test for 1.2.3 parsing #62 — updates to LibParseDecimalFloat.parseDecimalFloatInline precision checks related to the tightened parse validation.

Suggested reviewers

  • hardyjosh
  • 0xgleb
  • findolor

📜 Recent 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 c47e56f and cc1362f.

📒 Files selected for processing (1)
  • crates/float/src/lib.rs (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 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#119
File: test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol:174-175
Timestamp: 2025-09-02T09:33:32.485Z
Learning: The LibFormatDecimalFloat.toDecimalString function in src/lib/format/LibFormatDecimalFloat.sol does not include rounding logic. It formats decimal floats as-is without rounding values based on significant figures limits.
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.
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.
📚 Learning: 2025-06-18T09:10:41.740Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#57
File: crates/float/src/lib.rs:316-328
Timestamp: 2025-06-18T09:10:41.740Z
Learning: In the rain.math.float codebase, the user prefers implementing standard Rust traits (like Neg) rather than creating redundant public methods when the trait already provides the needed functionality. Float implements Copy, so reference usage with operators is not a concern.

Applied to files:

  • crates/float/src/lib.rs
⏰ 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-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • 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 (macos-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: git-clean
🔇 Additional comments (5)
crates/float/src/lib.rs (5)

1219-1219: LGTM: updated abs() expectation to scientific notation.

Expectation matches the new formatter behavior.


1224-1224: LGTM: consistent scientific-notation expectation for positive input.

Matches the new formatting path.


1244-1244: LGTM: negation formats using scientific notation as expected.

String form aligns with the updated formatter.


1249-1249: LGTM: double-negation round-trip preserves scientific notation and sign.

Expectation is correct under the new rules.


360-364: Avoid magic number for sigFigsLimit; extract into constant and expose configurable API

Replace the hard-coded U256::from(9) in format() with a named constant and (optionally) add a format_with_sig_figs(...) variant:

- let calldata = DecimalFloat::formatCall {- a,- sigFigsLimit: U256::from(9),- }+ let calldata = DecimalFloat::formatCall {+ a,+ sigFigsLimit: U256::from(SIGFIGS_LIMIT_DEFAULT),+ }
.abi_encode();

Add at module scope:

constSIGFIGS_LIMIT_DEFAULT:u64 = 9;

Optionally, expose:

pubfnformat_with_sig_figs(self,sig_figs_limit:u64) -> Result<String,FloatError>{letFloat(a) = self;let calldata = DecimalFloat::formatCall{ a,sigFigsLimit:U256::from(sig_figs_limit)}.abi_encode();execute_call(Bytes::from(calldata), |output| {let decoded = DecimalFloat::formatCall::abi_decode_returns(output.as_ref())?;Ok(decoded)})}

Confirm that 9 was chosen to match the on-chain formatter’s expected default.

✨ 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-30-format

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

@thedavidmeister
thedavidmeister changed the base branch from 2025-08-30-constant to mainSeptember 1, 2025 16:07

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (2)
src/lib/parse/LibParseDecimalFloat.sol (1)

83-99: Keep tighter overflow checks; remove debug logs and retain purity.

  • The switch to scale > 67 with post-mul overflow/width checks is fine.
  • console2 logging in a pure function must be removed.
  • Consider replacing magic 67 with a named constant (optional).

Apply:

- if (scale > 67 && signedCoefficient != 0) {+ if (scale > 67 && signedCoefficient != 0) {
return (ParseDecimalPrecisionLoss.selector, cursor, 0, 0);
}
- scale = 10 ** scale;- console2.logInt(signedCoefficient);- console2.log(scale);+ scale = 10 ** scale;
int256 rescaledIntValue = signedCoefficient * int256(scale);
- console2.logInt(rescaledIntValue);- if (- rescaledIntValue / int256(scale) != signedCoefficient- || int224(rescaledIntValue) != rescaledIntValue- ) {- console2.log("ParseDecimalPrecisionLoss");+ if (+ rescaledIntValue / int256(scale) != signedCoefficient+ || int224(rescaledIntValue) != rescaledIntValue+ ) {
return (ParseDecimalPrecisionLoss.selector, cursor, 0, 0);
}
signedCoefficient = rescaledIntValue + fracValue;

(Optional constant)

+uint256 constant MAX_FRAC_SCALE_NONZERO = 67;
...
- if (scale > 67 && signedCoefficient != 0) {+ if (scale > MAX_FRAC_SCALE_NONZERO && signedCoefficient != 0) {
test/src/lib/parse/LibParseDecimalFloat.t.sol (1)

19-199: Add boundary tests for 67/68 fractional digits.

Validate the new scale > 67 behavior explicitly.

Apply:

 contract LibParseDecimalFloatTest is Test {
@@
}
+ function testParseFractionalDigitsBoundary67Pass() external pure {+ // "1." + 66 zeros + "1" => 67 fractional digits, should pass+ string memory zeros = new string(66);+ for (uint256 i = 0; i < 66; i++) {+ bytes(zeros)[i] = "0";+ }+ string memory s = string(abi.encodePacked("1.", zeros, "1"));+ (bytes4 err, uint256 cursorAfter,,) = this.parseDecimalFloatInlineExternal(s);+ assertEq(err, bytes4(0));+ assertEq(cursorAfter, bytes(s).length);+ }++ function testParseFractionalDigitsBoundary68Fail() external pure {+ // "1." + 67 zeros + "1" => 68 fractional digits, should precision-revert+ string memory zeros = new string(67);+ for (uint256 i = 0; i < 67; i++) {+ bytes(zeros)[i] = "0";+ }+ string memory s = string(abi.encodePacked("1.", zeros, "1"));+ checkParseDecimalFloatFail(s, ParseDecimalPrecisionLoss.selector, bytes(s).length);+ }

Also applies to: 300-309, 392-410

📜 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 8d09556 and 219d780.

📒 Files selected for processing (5)
  • src/lib/format/LibFormatDecimalFloat.sol (1 hunks)
  • src/lib/implementation/LibDecimalFloatImplementation.sol (2 hunks)
  • src/lib/parse/LibParseDecimalFloat.sol (2 hunks)
  • test/src/lib/format/LibFormatDecimalFloat.t.sol (3 hunks)
  • test/src/lib/parse/LibParseDecimalFloat.t.sol (2 hunks)
🧰 Additional context used
🧠 Learnings (8)
📓 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#115
File: src/lib/implementation/LibDecimalFloatImplementation.sol:593-601
Timestamp: 2025-08-29T10:38:26.330Z
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/parse/LibParseDecimalFloat.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.t.sol
  • src/lib/format/LibFormatDecimalFloat.sol
  • src/lib/parse/LibParseDecimalFloat.sol
📚 Learning: 2025-08-29T10:38:26.330Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: src/lib/implementation/LibDecimalFloatImplementation.sol:593-601
Timestamp: 2025-08-29T10:38:26.330Z
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/parse/LibParseDecimalFloat.t.sol
  • src/lib/format/LibFormatDecimalFloat.sol
  • src/lib/parse/LibParseDecimalFloat.sol
📚 Learning: 2025-08-29T10:38:26.330Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: src/lib/implementation/LibDecimalFloatImplementation.sol:593-601
Timestamp: 2025-08-29T10:38:26.330Z
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
  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-29T14:58:50.463Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: src/lib/implementation/LibDecimalFloatImplementation.sol:896-899
Timestamp: 2025-08-29T14:58:50.463Z
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:

  • src/lib/implementation/LibDecimalFloatImplementation.sol
📚 Learning: 2025-08-29T14:54:24.211Z
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.211Z
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:

  • test/src/lib/parse/LibParseDecimalFloat.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.t.sol
  • src/lib/format/LibFormatDecimalFloat.sol
  • src/lib/parse/LibParseDecimalFloat.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/format/LibFormatDecimalFloat.t.sol
  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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-static)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-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 (macos-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: git-clean
🔇 Additional comments (15)
test/src/lib/parse/LibParseDecimalFloat.t.sol (2)

300-309: Good regression test.

Covers the formatter round-trip edge; assertions look correct.


405-409: Update aligns with new precision guard.

Test still asserts the intended precision-loss path.

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

28-35: Rename/readability LGTM.

Bound range and round-trip assertions look correct.


38-46: Negative formatting parity LGTM.

Sign handling via minus() and concat check is sound.


51-57: Positive scientific-format examples LGTM.

Expectations align with the new pipeline.


59-67: Zero fast-path coverage LGTM.

Good checks across varying exponents.


69-75: Negative scientific-format examples LGTM.

Mirror of positive cases looks consistent.


77-79: Boundary case for 1 LGTM.

Matches the maximize(1,0) special-case behavior.


81-82: Fuzz-derived example LGTM.

Covers leading-zero fractional padding with trimmed trailing zeros.

src/lib/format/LibFormatDecimalFloat.sol (6)

22-24: Zero fast-path LGTM.

Short-circuit avoids unnecessary work.


26-28: Use of maximize LGTM.

Correct entry point for normalized representation.


29-32: Dynamic scale selection LGTM.

Boundary at 1e76 matches the known maximize behavior.


33-43: Integral/fractional split with sign handling LGTM.

Negativity carried by integral; fractional made non-negative is correct.


57-61: Trailing-zero trim LGTM.

Simple and correct for integer math.


62-64: Fractional assembly LGTM.

Omits “.0” as intended.

Comment threadsrc/lib/format/LibFormatDecimalFloat.sol Outdated
Comment threadsrc/lib/format/LibFormatDecimalFloat.sol
Comment threadsrc/lib/format/LibFormatDecimalFloat.sol
Comment threadsrc/lib/format/LibFormatDecimalFloat.sol
Comment threadsrc/lib/format/LibFormatDecimalFloat.sol Outdated
Comment threadsrc/lib/implementation/LibDecimalFloatImplementation.sol Outdated
Comment threadsrc/lib/implementation/LibDecimalFloatImplementation.sol Outdated
Comment threadsrc/lib/parse/LibParseDecimalFloat.sol Outdated

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
test/src/lib/format/LibFormatDecimalFloat.t.sol (1)

38-46: Add tests for negative sub-unit values (-0.x cases).

Current suite won’t catch sign handling bugs when |value| < 1. Add explicit checks like -0.1 and -0.123 to guard regressions.

Apply:

 function testFormatDecimalExamples() external pure {
// pos decs
@@
// neg decs
checkFormat(-123456789012345678901234567890, 0, "-1.2345678901234567890123456789e29");
@@
checkFormat(-123456789012345678901234567890, -6, "-1.2345678901234567890123456789e23");
+ // neg fracs < 1+ checkFormat(-1, -1, "-0.1");+ checkFormat(-123, -3, "-0.123");
♻️ Duplicate comments (3)
src/lib/format/LibFormatDecimalFloat.sol (3)

7-11: Remove unused import; add SignedMath for int handling.

LibFixedPointDecimalFormat is unused; we need SignedMath for safe int->string handling.

Apply:

-import {LibFixedPointDecimalFormat} from "rain.math.fixedpoint/lib/format/LibFixedPointDecimalFormat.sol";
import {LibDecimalFloatImplementation} from "../../lib/implementation/LibDecimalFloatImplementation.sol";
import {Strings} from "openzeppelin-contracts/contracts/utils/Strings.sol";
+import {SignedMath} from "openzeppelin-contracts/contracts/utils/math/SignedMath.sol";

77-105: O(n^2) zero padding; switch to bytes buffer. Also cast fractional to uint for toString.

Repeated string.concat in a loop is quadratic; and Strings.toString expects uint.

Apply:

- string memory fracLeadingZerosString = "";+ string memory fracLeadingZerosString = "";
@@
- while (fractional / int256(fracScale) == 0) {+ while (fractional / int256(fracScale) == 0) {
fracScale /= 10;
fracLeadingZeros++;
}
- for (uint256 i = 0; i < fracLeadingZeros; i++) {- fracLeadingZerosString = string.concat(fracLeadingZerosString, "0");- }+ if (fracLeadingZeros != 0) {+ bytes memory zeros = new bytes(fracLeadingZeros);+ for (uint256 i = 0; i < fracLeadingZeros; i++) {+ zeros[i] = 0x30; // '0'+ }+ fracLeadingZerosString = string(zeros);+ }- while ((fractional / 10) * 10 == fractional) {+ while (fractional % 10 == 0) {
fractional /= 10;
}
}
fractionalString =
- fractional == 0 ? "" : string.concat(".", fracLeadingZerosString, Strings.toString(fractional));+ fractional == 0 ? "" : string.concat(".", fracLeadingZerosString, Strings.toString(uint256(fractional)));

41-46: Offchain-only helper in src/

If this is strictly offchain/test-only, consider moving it under test helpers to avoid bytecode bloat in deployable builds.

📜 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 219d780 and 760a229.

📒 Files selected for processing (2)
  • src/lib/format/LibFormatDecimalFloat.sol (1 hunks)
  • test/src/lib/format/LibFormatDecimalFloat.t.sol (3 hunks)
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-08-29T14:54:24.211Z
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.211Z
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/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.t.sol
📚 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/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.t.sol
📚 Learning: 2025-08-29T10:38:26.330Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: src/lib/implementation/LibDecimalFloatImplementation.sol:593-601
Timestamp: 2025-08-29T10:38:26.330Z
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/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-29T10:38:26.330Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: src/lib/implementation/LibDecimalFloatImplementation.sol:593-601
Timestamp: 2025-08-29T10:38:26.330Z
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/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 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/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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:

  • src/lib/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.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, test-wasm-build)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (macos-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: git-clean
🔇 Additional comments (7)
test/src/lib/format/LibFormatDecimalFloat.t.sol (6)

28-35: Rename + bounds LGTM

Function rename is clear; bounds look sane for fuzzing.


51-57: Scientific notation expectations LGTM

Examples align with normalized 1.xeN formatting.


59-67: Zero formatting LGTM

Consistently returns "0" across exponents.


68-75: Negative scientific examples LGTM

Mirrors positive cases with correct sign.


77-85: “100” equivalence set LGTM

Good coverage across coefficient/exponent combos.


86-88: Fuzz-derived case LGTM

Matches expected e-6 scaling.

src/lib/format/LibFormatDecimalFloat.sol (1)

13-19: Edge-case: abs(int256.min).

Both here and later abs operations can overflow for type(int256).min. If LibDecimalFloat guarantees coefficient != min, document it; otherwise guard with a special-case.

Would you like a patch that adds a safeAbs helper handling int256.min with uint-return semantics and uses it throughout?

Comment threadsrc/lib/format/LibFormatDecimalFloat.sol
Comment threadsrc/lib/format/LibFormatDecimalFloat.sol
Comment threadsrc/lib/format/LibFormatDecimalFloat.sol

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/lib/format/LibFormatDecimalFloat.sol (1)

73-82: Fix uint cast underflow and potential int cast revert; only build exponent when scientific.

In the non-scientific branch, scaleExponent = uint256(exponent) underflows for negative exponent, and later int256(scaleExponent) can revert. Also, there’s no need to compute/display the exponent when !scientific.

Apply:

 } else {
- if (exponent > 0) {- signedCoefficient *= int256(10) ** uint256(exponent);- exponent = 0;- }- if (exponent < 0) {- scale = uint256(10) ** uint256(-exponent);- }- scaleExponent = uint256(exponent);+ if (exponent > 0) {+ // Safe given sigFigsLimit gating; product stays within int256 bounds.+ signedCoefficient *= int256(10) ** uint256(exponent);+ exponent = 0;+ } else if (exponent < 0) {+ scale = uint256(10) ** uint256(-exponent);+ }+ // Not used when !scientific.+ scaleExponent = 0;
}
@@
- int256 displayExponent = exponent + int256(scaleExponent);- string memory exponentString =- (displayExponent == 0 || !scientific) ? "" : string.concat("e", Strings.toString(displayExponent));+ string memory exponentString = "";+ if (scientific) {+ int256 displayExponent = exponent + int256(scaleExponent);+ if (displayExponent != 0) {+ exponentString = string.concat("e", Strings.toString(displayExponent));+ }+ }

Also applies to: 123-126

📜 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 58979ec and 31ae5a7.

📒 Files selected for processing (1)
  • src/lib/format/LibFormatDecimalFloat.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (18)
📓 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-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/format/LibFormatDecimalFloat.sol
📚 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/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-09-01T19:11:36.597Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:0-0
Timestamp: 2025-09-01T19:11:36.597Z
Learning: In the rainlanguage/rain.math.float project, the codebase targets a specific OpenZeppelin version rather than maintaining compatibility across arbitrary OZ versions. Direct usage of Strings.toString on signed integers is acceptable within this versioning approach.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 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/format/LibFormatDecimalFloat.sol
📚 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:

  • src/lib/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-14T16:56:28.978Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#99
File: src/lib/implementation/LibDecimalFloatImplementation.sol:574-581
Timestamp: 2025-08-14T16:56:28.978Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister avoids using `&&` operators in gas-critical paths because they involve jumps in Solidity due to short-circuit evaluation, preferring approaches like mul-then-div overflow probes that avoid conditional jump overhead.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-27T13:37:22.601Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol:126-133
Timestamp: 2025-08-27T13:37:22.601Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister prefers to avoid inline `if` statements (even if they were supported in Solidity) because they create fragile code with meaningful whitespace and make debugging difficult when console logs need to be added, potentially causing subtle behavior changes if braces aren't reintroduced properly.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-11T14:32:50.439Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/implementation/LibDecimalFloatImplementation.maximize.t.sol:15-29
Timestamp: 2025-08-11T14:32:50.439Z
Learning: In test code for the rain.math.float repository, redundant checks may be intentionally kept for clarity and documentation purposes, even when they could be simplified. The maintainer (thedavidmeister) prefers explicit assertions in test code to make the test's intent clear to future readers, prioritizing readability over conciseness.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-06-17T10:17:56.205Z
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.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-26T15:50:31.262Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#110
File: src/lib/implementation/LibDecimalFloatImplementation.sol:1011-1011
Timestamp: 2025-08-26T15:50:31.262Z
Learning: In the rain.math.float repository, thedavidmeister prefers concise, high-level documentation comments that capture intent rather than detailed comments that mirror implementation specifics. Detailed implementation-focused comments are considered fragile because they become outdated when code changes but comments aren't updated.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-07-03T11:20:50.456Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#70
File: crates/float/src/evm.rs:38-43
Timestamp: 2025-07-03T11:20:50.456Z
Learning: In the rainlanguage/rain.math.float codebase, the user 0xgleb prefers not to add explanatory comments for well-established Rust idioms like the double `?` pattern, as these are self-explanatory to experienced Rust developers and don't need over-commenting.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-09-01T18:59:30.602Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:99-110
Timestamp: 2025-09-01T18:59:30.602Z
Learning: In Solidity, you cannot cast arrays to strings due to type system limitations. String concatenation optimizations using bytes arrays and casting aren't viable because of fundamental differences in how Solidity handles these data types - strings consist of individual bytes while arrays contain 32-byte integers, making direct casting impossible.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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-sol-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: rainix (macos-latest, rainix-sol-legal)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: git-clean
🔇 Additional comments (3)
src/lib/format/LibFormatDecimalFloat.sol (3)

46-49: Default sig figs + early-zero return: good guardrails.

Using a small DEFAULT_SIG_FIGS and short-circuiting on zero simplifies downstream logic.

Also applies to: 59-65


84-95: Sign normalization correctly preserves “-0.x”.

Deriving prefix from the sign after splitting integral/fractional avoids losing the sign for magnitudes < 1.


96-119: Edge-case sanity: fractional block assumes scale > 0 when fractional != 0.

This holds today, but if upstream invariants change, fracScale = scale / 10 could be 0. Consider a minimal guard or test to lock this in.

Would you like me to add a test that exercises exponent = {-1, -2} with tiny coefficients to ensure fracScale never reaches 0 while fractional != 0?

Comment threadsrc/lib/format/LibFormatDecimalFloat.sol

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 3

♻️ Duplicate comments (4)
src/lib/format/LibFormatDecimalFloat.sol (4)

81-81: Incorrect handling of scaleExponent for negative exponents in non-scientific mode.

When exponent < 0 in non-scientific mode, line 81 incorrectly assigns scaleExponent = uint256(exponent), which will underflow since exponent is negative. This appears to be dead code since scaleExponent is only used in scientific mode, but it could cause issues if the logic changes.

- scaleExponent = uint256(exponent);+ scaleExponent = 0; // Not used in non-scientific mode

8-10: Remove unused import.

The LibFixedPointDecimalFormat import on line 7 is not used anywhere in the code.

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

123-125: Potential issue with Strings.toString on signed integer.

The code calls Strings.toString(displayExponent) where displayExponent is an int256. As noted in the retrieved learnings, this targets a specific OpenZeppelin version. However, displayExponent can be negative, which needs explicit handling.

 int256 displayExponent = exponent + int256(scaleExponent);
-string memory exponentString =- (displayExponent == 0 || !scientific) ? "" : string.concat("e", Strings.toString(displayExponent));+string memory exponentString = "";+if (scientific && displayExponent != 0) {+ if (displayExponent > 0) {+ exponentString = string.concat("e", Strings.toString(uint256(displayExponent)));+ } else {+ exponentString = string.concat("e-", Strings.toString(uint256(-displayExponent)));+ }+}

121-121: Missing explicit cast for Strings.toString call.

Similar to the above, integral is an int256 that's been made non-negative, but should be explicitly cast.

-string memory integralString = Strings.toString(integral);+string memory integralString = Strings.toString(uint256(integral));
📜 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 293a544 and c33481e.

📒 Files selected for processing (1)
  • src/lib/format/LibFormatDecimalFloat.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (18)
📓 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-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/format/LibFormatDecimalFloat.sol
📚 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/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-09-01T19:11:36.614Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:0-0
Timestamp: 2025-09-01T19:11:36.614Z
Learning: In the rainlanguage/rain.math.float project, the codebase targets a specific OpenZeppelin version rather than maintaining compatibility across arbitrary OZ versions. Direct usage of Strings.toString on signed integers is acceptable within this versioning approach.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 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/format/LibFormatDecimalFloat.sol
📚 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:

  • src/lib/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-14T16:56:28.978Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#99
File: src/lib/implementation/LibDecimalFloatImplementation.sol:574-581
Timestamp: 2025-08-14T16:56:28.978Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister avoids using `&&` operators in gas-critical paths because they involve jumps in Solidity due to short-circuit evaluation, preferring approaches like mul-then-div overflow probes that avoid conditional jump overhead.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-27T13:37:22.601Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol:126-133
Timestamp: 2025-08-27T13:37:22.601Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister prefers to avoid inline `if` statements (even if they were supported in Solidity) because they create fragile code with meaningful whitespace and make debugging difficult when console logs need to be added, potentially causing subtle behavior changes if braces aren't reintroduced properly.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-11T14:32:50.439Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/implementation/LibDecimalFloatImplementation.maximize.t.sol:15-29
Timestamp: 2025-08-11T14:32:50.439Z
Learning: In test code for the rain.math.float repository, redundant checks may be intentionally kept for clarity and documentation purposes, even when they could be simplified. The maintainer (thedavidmeister) prefers explicit assertions in test code to make the test's intent clear to future readers, prioritizing readability over conciseness.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-06-17T10:17:56.205Z
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.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-26T15:50:31.262Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#110
File: src/lib/implementation/LibDecimalFloatImplementation.sol:1011-1011
Timestamp: 2025-08-26T15:50:31.262Z
Learning: In the rain.math.float repository, thedavidmeister prefers concise, high-level documentation comments that capture intent rather than detailed comments that mirror implementation specifics. Detailed implementation-focused comments are considered fragile because they become outdated when code changes but comments aren't updated.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-07-03T11:20:50.456Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#70
File: crates/float/src/evm.rs:38-43
Timestamp: 2025-07-03T11:20:50.456Z
Learning: In the rainlanguage/rain.math.float codebase, the user 0xgleb prefers not to add explanatory comments for well-established Rust idioms like the double `?` pattern, as these are self-explanatory to experienced Rust developers and don't need over-commenting.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-09-01T18:59:30.642Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:99-110
Timestamp: 2025-09-01T18:59:30.642Z
Learning: In Solidity, you cannot cast arrays to strings due to type system limitations. String concatenation optimizations using bytes arrays and casting aren't viable because of fundamental differences in how Solidity handles these data types - strings consist of individual bytes while arrays contain 32-byte integers, making direct casting impossible.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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:

  • src/lib/format/LibFormatDecimalFloat.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 (macos-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • GitHub Check: rainix (macos-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: git-clean
🔇 Additional comments (2)
src/lib/format/LibFormatDecimalFloat.sol (2)

70-72: LGTM!

The scientific notation detection and scale calculation logic is well-implemented. The use of signedCoefficient / 1e76 != 0 to check magnitude is efficient.


84-95: LGTM!

The sign normalization logic correctly handles both positive and negative values, ensuring the sign is only applied once as a prefix.

Comment threadsrc/lib/format/LibFormatDecimalFloat.sol
Comment threadsrc/lib/format/LibFormatDecimalFloat.sol

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 2

♻️ Duplicate comments (3)
src/lib/format/LibFormatDecimalFloat.sol (3)

7-7: Remove unused import.

The LibFixedPointDecimalFormat import is not used anywhere in this file.

Apply this diff to remove the unused import:

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

117-117: Explicit cast to uint256 for clarity.

While fractional is guaranteed to be non-negative at this point (lines 90-93), it's clearer to explicitly cast it to uint256 when calling Strings.toString.

Apply this diff for clarity:

 fractionalString =
- fractional == 0 ? "" : string.concat(".", fracLeadingZerosString, Strings.toString(fractional));+ fractional == 0 ? "" : string.concat(".", fracLeadingZerosString, Strings.toString(uint256(fractional)));

15-43: Potential integer overflow in countSigFigs.

The function operates on a mutable signedCoefficient parameter and performs modulo operations directly on it without handling the edge case where signedCoefficient == type(int256).min. For this value, the absolute value cannot be represented as a positive int256, which could lead to unexpected behavior.

Apply this diff to handle the edge case safely:

 function countSigFigs(int256 signedCoefficient, int256 exponent) internal pure returns (uint256) {
if (signedCoefficient == 0) {
return 1;
}
+ uint256 absCoefficient;+ if (signedCoefficient == type(int256).min) {+ absCoefficient = uint256(type(int256).max) + 1;+ } else {+ absCoefficient = uint256(signedCoefficient < 0 ? -signedCoefficient : signedCoefficient);+ }
uint256 sigFigs = 0;
if (exponent < 0) {
- while (signedCoefficient % 10 == 0) {- signedCoefficient /= 10;+ while (absCoefficient % 10 == 0) {+ absCoefficient /= 10;
exponent++;
}
}
- while (signedCoefficient != 0) {+ while (absCoefficient != 0) {
sigFigs++;
- signedCoefficient /= 10;+ absCoefficient /= 10;
}
📜 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 c33481e and 5f0b756.

📒 Files selected for processing (1)
  • src/lib/format/LibFormatDecimalFloat.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (18)
📚 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/format/LibFormatDecimalFloat.sol
📚 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/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-09-01T19:11:36.614Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:0-0
Timestamp: 2025-09-01T19:11:36.614Z
Learning: In the rainlanguage/rain.math.float project, the codebase targets a specific OpenZeppelin version rather than maintaining compatibility across arbitrary OZ versions. Direct usage of Strings.toString on signed integers is acceptable within this versioning approach.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 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/format/LibFormatDecimalFloat.sol
📚 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:

  • src/lib/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-14T16:56:28.978Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#99
File: src/lib/implementation/LibDecimalFloatImplementation.sol:574-581
Timestamp: 2025-08-14T16:56:28.978Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister avoids using `&&` operators in gas-critical paths because they involve jumps in Solidity due to short-circuit evaluation, preferring approaches like mul-then-div overflow probes that avoid conditional jump overhead.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-27T13:37:22.601Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol:126-133
Timestamp: 2025-08-27T13:37:22.601Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister prefers to avoid inline `if` statements (even if they were supported in Solidity) because they create fragile code with meaningful whitespace and make debugging difficult when console logs need to be added, potentially causing subtle behavior changes if braces aren't reintroduced properly.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-11T14:32:50.439Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/implementation/LibDecimalFloatImplementation.maximize.t.sol:15-29
Timestamp: 2025-08-11T14:32:50.439Z
Learning: In test code for the rain.math.float repository, redundant checks may be intentionally kept for clarity and documentation purposes, even when they could be simplified. The maintainer (thedavidmeister) prefers explicit assertions in test code to make the test's intent clear to future readers, prioritizing readability over conciseness.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-06-17T10:17:56.205Z
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.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-26T15:50:31.262Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#110
File: src/lib/implementation/LibDecimalFloatImplementation.sol:1011-1011
Timestamp: 2025-08-26T15:50:31.262Z
Learning: In the rain.math.float repository, thedavidmeister prefers concise, high-level documentation comments that capture intent rather than detailed comments that mirror implementation specifics. Detailed implementation-focused comments are considered fragile because they become outdated when code changes but comments aren't updated.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-07-03T11:20:50.456Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#70
File: crates/float/src/evm.rs:38-43
Timestamp: 2025-07-03T11:20:50.456Z
Learning: In the rainlanguage/rain.math.float codebase, the user 0xgleb prefers not to add explanatory comments for well-established Rust idioms like the double `?` pattern, as these are self-explanatory to experienced Rust developers and don't need over-commenting.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-09-01T18:59:30.642Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:99-110
Timestamp: 2025-09-01T18:59:30.642Z
Learning: In Solidity, you cannot cast arrays to strings due to type system limitations. String concatenation optimizations using bytes arrays and casting aren't viable because of fundamental differences in how Solidity handles these data types - strings consist of individual bytes while arrays contain 32-byte integers, making direct casting impossible.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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:

  • src/lib/format/LibFormatDecimalFloat.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: 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/format/LibFormatDecimalFloat.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). (2)
  • GitHub Check: rainix (macos-latest, rainix-sol-legal)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
🔇 Additional comments (1)
src/lib/format/LibFormatDecimalFloat.sol (1)

120-120: Type safety: Strings.toString expects uint256, not int256.

According to my previous learning, the codebase targets a specific OpenZeppelin version where direct usage of Strings.toString on signed integers is acceptable. However, I want to verify that integral (line 120) and displayExponent (line 124) are correctly handled.

The code correctly ensures integral is non-negative before calling Strings.toString (lines 86-89), and the OpenZeppelin version used supports Strings.toString(int256) for displayExponent.

Also applies to: 124-124

Comment threadsrc/lib/format/LibFormatDecimalFloat.sol Outdated

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 5

📜 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 5f0b756 and 26d7ea2.

📒 Files selected for processing (2)
  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol (1 hunks)
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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: 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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.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/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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: git-clean
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: rainix (macos-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
🔇 Additional comments (5)
test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol (2)

11-14: Good helper: focused assertion wrapper.

Straightforward, keeps the tests readable and precise.


92-94: Zero case coverage is crisp.

Asserting 1 sig fig for zero across all exponents aligns with conventional formatting expectations.

test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (3)

17-21: Helper is clean and reusable.

Good abstraction for constructing and asserting format expectations.


59-67: Zero formatting looks correct and comprehensive.

Covers multiple exponents mapping to canonical "0".


128-135: Nice cutoff validation for 9 sig figs.

Good to pin behavior at the threshold before scientific notation kicks in.

Comment threadtest/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
Comment threadtest/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
Comment threadtest/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol Outdated
Comment threadtest/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol Outdated

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 1

♻️ Duplicate comments (5)
src/lib/format/LibFormatDecimalFloat.sol (5)

118-119: Explicit cast needed for Strings.toString(fractional)

Line 119 calls Strings.toString(fractional) where fractional is an int256. While the code ensures fractional is non-negative at this point, it's clearer and safer to explicitly cast it to uint256.

Apply this diff for clarity:

 fractionalString =
- fractional == 0 ? "" : string.concat(".", fracLeadingZerosString, Strings.toString(fractional));+ fractional == 0 ? "" : string.concat(".", fracLeadingZerosString, Strings.toString(uint256(fractional)));

7-10: Remove unused import and ensure broad OpenZeppelin compatibility

The LibFixedPointDecimalFormat import on line 7 is unused in this implementation. Additionally, as noted in past reviews and learnings, using Strings.toString() directly on signed integers may cause compatibility issues across different OpenZeppelin versions.

Apply this diff to clean up imports:

-import {LibFixedPointDecimalFormat} from "rain.math.fixedpoint/lib/format/LibFixedPointDecimalFormat.sol";
import {LibDecimalFloatImplementation} from "../../lib/implementation/LibDecimalFloatImplementation.sol";
import {Strings} from "openzeppelin-contracts/contracts/utils/Strings.sol";

122-126: Cast required for Strings.toString with signed integers

Lines 122 and 126 call Strings.toString with int256 values (integral and displayExponent). Since Strings.toString expects uint256, these will cause compilation errors. The code needs explicit handling for signed values.

Apply this diff to fix the compilation errors:

- string memory integralString = Strings.toString(integral);+ string memory integralString = Strings.toString(uint256(integral));
int256 displayExponent = exponent + int256(scaleExponent);
- string memory exponentString =- (displayExponent == 0 || !scientific) ? "" : string.concat("e", Strings.toString(displayExponent));+ string memory exponentString = "";+ if (scientific && displayExponent != 0) {+ if (displayExponent > 0) {+ exponentString = string.concat("e", Strings.toString(uint256(displayExponent)));+ } else {+ exponentString = string.concat("e-", Strings.toString(uint256(-displayExponent)));+ }+ }

15-43: Potential integer overflow in countSigFigs with type(int256).min

The function mutates signedCoefficient directly and uses modulo operations without handling the edge case where signedCoefficient == type(int256).min. When this value is divided or used in modulo operations, it could cause unexpected behavior since its absolute value cannot be represented as a positive int256.

Apply this diff to work with the absolute value safely:

 function countSigFigs(int256 signedCoefficient, int256 exponent) internal pure returns (uint256) {
if (signedCoefficient == 0) {
return 1;
}
+ uint256 absCoefficient = signedCoefficient == type(int256).min + ? uint256(type(int256).max) + 1 + : uint256(signedCoefficient < 0 ? -signedCoefficient : signedCoefficient);
uint256 sigFigs = 0;
if (exponent < 0) {
- while (signedCoefficient % 10 == 0) {- signedCoefficient /= 10;+ while (absCoefficient % 10 == 0) {+ absCoefficient /= 10;
exponent++;
}
}
- while (signedCoefficient != 0) {+ while (absCoefficient != 0) {
sigFigs++;
- signedCoefficient /= 10;+ absCoefficient /= 10;
}

79-81: Incorrect scaleExponent assignment for non-negative exponents

When exponent >= 0 in non-scientific mode, the code assigns scaleExponent = uint256(exponent). Since exponent is 0 at this point (after line 75), this is correct but the else branch at line 81 is unreachable. More importantly, the logic seems unclear about what scaleExponent should represent in this path.

Apply this diff to clarify the logic:

- if (exponent < 0) {- scale = uint256(10) ** uint256(-exponent);- scaleExponent = uint256(-exponent);- } else {- scaleExponent = uint256(exponent);- }+ if (exponent < 0) {+ scale = uint256(10) ** uint256(-exponent);+ scaleExponent = uint256(-exponent);+ } else {+ scaleExponent = 0;+ }
📜 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 26d7ea2 and 3dbe92b.

📒 Files selected for processing (1)
  • src/lib/format/LibFormatDecimalFloat.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (19)
📓 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#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-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/format/LibFormatDecimalFloat.sol
📚 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/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-09-01T19:11:36.614Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:0-0
Timestamp: 2025-09-01T19:11:36.614Z
Learning: In the rainlanguage/rain.math.float project, the codebase targets a specific OpenZeppelin version rather than maintaining compatibility across arbitrary OZ versions. Direct usage of Strings.toString on signed integers is acceptable within this versioning approach.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 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/format/LibFormatDecimalFloat.sol
📚 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:

  • src/lib/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-14T16:56:28.978Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#99
File: src/lib/implementation/LibDecimalFloatImplementation.sol:574-581
Timestamp: 2025-08-14T16:56:28.978Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister avoids using `&&` operators in gas-critical paths because they involve jumps in Solidity due to short-circuit evaluation, preferring approaches like mul-then-div overflow probes that avoid conditional jump overhead.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-27T13:37:22.601Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol:126-133
Timestamp: 2025-08-27T13:37:22.601Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister prefers to avoid inline `if` statements (even if they were supported in Solidity) because they create fragile code with meaningful whitespace and make debugging difficult when console logs need to be added, potentially causing subtle behavior changes if braces aren't reintroduced properly.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-11T14:32:50.439Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/implementation/LibDecimalFloatImplementation.maximize.t.sol:15-29
Timestamp: 2025-08-11T14:32:50.439Z
Learning: In test code for the rain.math.float repository, redundant checks may be intentionally kept for clarity and documentation purposes, even when they could be simplified. The maintainer (thedavidmeister) prefers explicit assertions in test code to make the test's intent clear to future readers, prioritizing readability over conciseness.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-06-17T10:17:56.205Z
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.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-26T15:50:31.262Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#110
File: src/lib/implementation/LibDecimalFloatImplementation.sol:1011-1011
Timestamp: 2025-08-26T15:50:31.262Z
Learning: In the rain.math.float repository, thedavidmeister prefers concise, high-level documentation comments that capture intent rather than detailed comments that mirror implementation specifics. Detailed implementation-focused comments are considered fragile because they become outdated when code changes but comments aren't updated.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-07-03T11:20:50.456Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#70
File: crates/float/src/evm.rs:38-43
Timestamp: 2025-07-03T11:20:50.456Z
Learning: In the rainlanguage/rain.math.float codebase, the user 0xgleb prefers not to add explanatory comments for well-established Rust idioms like the double `?` pattern, as these are self-explanatory to experienced Rust developers and don't need over-commenting.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-09-01T18:59:30.642Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:99-110
Timestamp: 2025-09-01T18:59:30.642Z
Learning: In Solidity, you cannot cast arrays to strings due to type system limitations. String concatenation optimizations using bytes arrays and casting aren't viable because of fundamental differences in how Solidity handles these data types - strings consist of individual bytes while arrays contain 32-byte integers, making direct casting impossible.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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:

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

13-48: Well-designed significant figures handling

The introduction of DEFAULT_SIG_FIGS constant and the countSigFigs helper function provides a clean abstraction for determining when to use scientific notation. The overloaded toDecimalString maintaining backward compatibility while adding the new functionality is a good design choice.

Comment threadsrc/lib/format/LibFormatDecimalFloat.sol

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 3

♻️ Duplicate comments (2)
test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol (1)

109-114: Document the [-76, 0] bound rationale.

Add a short note about int256 limits and alignment with maximize/log10 edge cases.

 function testCountSigFigsOne(int256 exponent) external pure {
- exponent = bound(exponent, -76, 0);+ // Bound to [-76, 0] so 10**(-exponent) <= 1e76 fits within int256 max (~1.15e77),+ // and matches maximize/log10 edge-case handling.+ exponent = bound(exponent, -76, 0);
int256 one = int256(10 ** uint256(-exponent));
test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1)

41-49: Unify constructor with positive test and assert round-trip on negative.

Use fromFixedDecimalLosslessPacked for consistency and parse/eq the negative string too.

 function testFormatDecimalRoundTripNegative(int256 value) external pure {
value = bound(value, 1, int256(type(int128).max));
- Float float = LibDecimalFloat.packLossless(value, 18);+ Float float = LibDecimalFloat.fromFixedDecimalLosslessPacked(uint256(value), 18);
string memory formatted = float.toDecimalString();
float = float.minus();
string memory formattedNeg = float.toDecimalString();
assertEq(string.concat("-", formatted), formattedNeg, "Negative format mismatch");
+ // Parse/eq for negative path as well+ (bytes4 err, Float parsedNeg) = LibParseDecimalFloat.parseDecimalFloat(formattedNeg);+ assertEq(err, 0, "Parse error (neg)");+ assertTrue(float.eq(parsedNeg), "Round trip failed (neg)");
}
📜 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 3dbe92b and 97efb42.

📒 Files selected for processing (2)
  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol (1 hunks)
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (14)
📓 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-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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
📚 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/format/LibFormatDecimalFloat.countSigFigs.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: 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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-29T15:26:25.984Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: test/src/lib/LibDecimalFloat.decimal.t.sol:56-56
Timestamp: 2025-08-29T15:26:25.984Z
Learning: In Forge tests, the bound() function is overloaded and requires explicit type casting for overload resolution. When using bound(value, min, max), if the max parameter involves type conversions, an outer cast on the entire bound() call may be necessary for the compiler to resolve which overload to use. Additionally, Solidity cannot change both signedness and bit width in a single cast, so complex casts like uint256(int256(type(int224).max)) require the staged approach rather than a direct cast.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
📚 Learning: 2025-08-11T14:32:50.439Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/implementation/LibDecimalFloatImplementation.maximize.t.sol:15-29
Timestamp: 2025-08-11T14:32:50.439Z
Learning: In test code for the rain.math.float repository, redundant checks may be intentionally kept for clarity and documentation purposes, even when they could be simplified. The maintainer (thedavidmeister) prefers explicit assertions in test code to make the test's intent clear to future readers, prioritizing readability over conciseness.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.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/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T16:50:11.113Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/LibDecimalFloat.div.t.sol:48-51
Timestamp: 2025-08-26T16:50:11.113Z
Learning: In LibDecimalFloat, packLossless internally asserts that the packing operation is lossless, so there's no need to manually check the lossless flag when using packLossless instead of packLossy.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-06-17T10:17:56.205Z
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.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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-static)
  • GitHub Check: rainix (macos-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • 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: rainix (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • GitHub Check: git-clean
🔇 Additional comments (3)
test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol (1)

9-14: Good structure and coverage for core cases.

Helper + example/zero/one tests are clear and minimal.

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

35-38: Nice canonicalization check.

Idempotent format(parse(format(x))) assertion is valuable here.


150-158: Good boundary coverage (pure powers, extreme exponents).

These directly exercise DEFAULT_SIG_FIGS cutover and scientific selection.

Comment threadtest/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
Comment threadtest/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 4

♻️ Duplicate comments (1)
test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1)

162-163: Add an explicit‑sig‑figs overload test.

Exercise toDecimalString(Float, uint256) and rounding behavior.

 }
+ function testFormatDecimalCustomSigFigs() external pure {+ // More precision than requested; should round and select scientific.+ Float f = LibDecimalFloat.packLossless(1234567890, 0);+ string memory s = LibFormatDecimalFloat.toDecimalString(f, 5);+ assertEq(s, "1.2346e9", "Custom sig-figs not applied");+ }

Confirm the overload exists:

#!/bin/bash
rg -nP 'toDecimalString\s*\(\s*Float[^,]*,\s*uint256\s*\)' src
📜 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 97efb42 and 2ae4f0c.

📒 Files selected for processing (1)
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (12)
📓 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-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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T16:50:11.113Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/LibDecimalFloat.div.t.sol:48-51
Timestamp: 2025-08-26T16:50:11.113Z
Learning: In LibDecimalFloat, packLossless internally asserts that the packing operation is lossless, so there's no need to manually check the lossless flag when using packLossless instead of packLossy.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-11T14:32:50.439Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/implementation/LibDecimalFloatImplementation.maximize.t.sol:15-29
Timestamp: 2025-08-11T14:32:50.439Z
Learning: In test code for the rain.math.float repository, redundant checks may be intentionally kept for clarity and documentation purposes, even when they could be simplified. The maintainer (thedavidmeister) prefers explicit assertions in test code to make the test's intent clear to future readers, prioritizing readability over conciseness.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-06-17T10:17:56.205Z
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.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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: 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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-27T13:37:22.601Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol:126-133
Timestamp: 2025-08-27T13:37:22.601Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister prefers to avoid inline `if` statements (even if they were supported in Solidity) because they create fragile code with meaningful whitespace and make debugging difficult when console logs need to be added, potentially causing subtle behavior changes if braces aren't reintroduced properly.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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). (6)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: rainix (macos-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
🔇 Additional comments (7)
test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (7)

10-16: Good test scaffold and use of both call sites.

Covers both library-call and extension-method paths.


17-21: Helper is clear and focused.

Using packLossless here is appropriate to target coefficient/exponent normalization.


27-38: Round‑trip + canonicalization looks solid.

Nice fuzz boundary, parse check, and idempotent formatting assertion.


55-65: Large decimal cases LGTM.

Good coverage of magnitude shifts with explicit exponents.


66-74: Zero handling is exhaustive and clear.

Confirms canonical “0” across exponent variants.


135-149: 9→10 significant‑figures boundary well covered.

These examples exercise the switch to scientific notation.


154-161: Cutoff and extreme exponent boundaries covered.

Nice inclusion of pure powers and ±76 extremes.

Comment threadtest/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol Outdated
Comment threadtest/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol Outdated

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 5

♻️ Duplicate comments (5)
src/lib/format/LibFormatDecimalFloat.sol (2)

7-7: Remove unused import.

LibFixedPointDecimalFormat isn’t referenced. Drop it to keep the lib minimal.

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

117-119: Minor: build exponent string only when scientific.

Skip computing/displaying displayExponent when not in scientific mode.

- int256 displayExponent = exponent + int256(scaleExponent);- string memory exponentString =- (displayExponent == 0 || !scientific) ? "" : string.concat("e", Strings.toString(displayExponent));+ string memory exponentString = "";+ if (scientific) {+ int256 displayExponent = exponent + int256(scaleExponent);+ if (displayExponent != 0) {+ exponentString = string.concat("e", Strings.toString(displayExponent));+ }+ }
test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (3)

27-29: Document the test-only wrapper.

Add a short comment to clarify why this external is exposed.

- function toString(Float float, uint256 sigFigsLimit) external pure returns (string memory) {+ // Expose toDecimalString for testing only.+ function toString(Float float, uint256 sigFigsLimit) external pure returns (string memory) {
return LibFormatDecimalFloat.toDecimalString(float, sigFigsLimit);
}

44-57: Also assert canonicalization on the negative path.

Parities with the positive test and prevents sign/format drift after parse.

 (bytes4 err, Float parsedNeg) = LibParseDecimalFloat.parseDecimalFloat(formattedNeg);
assertEq(err, 0, "Parse error (neg)");
assertTrue(float.eq(parsedNeg), "Round trip failed (neg)");
+ // Canonicalization: format(parse(s)) == s for negatives too.+ string memory reFormattedNeg = LibFormatDecimalFloat.toDecimalString(parsedNeg, sigFigsLimit);+ assertEq(formattedNeg, reFormattedNeg, "Formatting not canonical (neg)");

59-166: Add a negative-zero canonicalization check.

Ensure “-0” never appears.

 // zeros
checkFormat(0, 0, 9, "0");
checkFormat(0, -1, 9, "0");
checkFormat(0, -2, 9, "0");
checkFormat(0, -3, 9, "0");
checkFormat(0, 1, 9, "0");
checkFormat(0, 2, 9, "0");
checkFormat(0, 3, 9, "0");
++ // "-0" should canonicalize to "0".+ {+ Float z = LibDecimalFloat.packLossless(0, 0);+ string memory s = z.minus().toDecimalString(9);+ assertEq(s, "0", "Negative zero should canonicalize to 0");+ }
📜 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 2ae4f0c and 6455a0a.

📒 Files selected for processing (4)
  • src/concrete/DecimalFloat.sol (1 hunks)
  • src/lib/format/LibFormatDecimalFloat.sol (1 hunks)
  • test/src/concrete/DecimalFloat.format.t.sol (1 hunks)
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (21)
📓 Common learnings
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol:174-175
Timestamp: 2025-09-02T09:33:32.485Z
Learning: The LibFormatDecimalFloat.toDecimalString function in src/lib/format/LibFormatDecimalFloat.sol does not include rounding logic. It formats decimal floats as-is without rounding values based on significant figures limits.
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#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: 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-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • src/concrete/DecimalFloat.sol
  • src/lib/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
  • test/src/concrete/DecimalFloat.format.t.sol
📚 Learning: 2025-09-02T09:33:32.485Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol:174-175
Timestamp: 2025-09-02T09:33:32.485Z
Learning: The LibFormatDecimalFloat.toDecimalString function in src/lib/format/LibFormatDecimalFloat.sol does not include rounding logic. It formats decimal floats as-is without rounding values based on significant figures limits.

Applied to files:

  • src/concrete/DecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
  • test/src/concrete/DecimalFloat.format.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/format/LibFormatDecimalFloat.sol
📚 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/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
  • test/src/concrete/DecimalFloat.format.t.sol
📚 Learning: 2025-09-01T19:11:36.614Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:0-0
Timestamp: 2025-09-01T19:11:36.614Z
Learning: In the rainlanguage/rain.math.float project, the codebase targets a specific OpenZeppelin version rather than maintaining compatibility across arbitrary OZ versions. Direct usage of Strings.toString on signed integers is acceptable within this versioning approach.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
📚 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/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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:

  • src/lib/format/LibFormatDecimalFloat.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/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-14T16:56:28.978Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#99
File: src/lib/implementation/LibDecimalFloatImplementation.sol:574-581
Timestamp: 2025-08-14T16:56:28.978Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister avoids using `&&` operators in gas-critical paths because they involve jumps in Solidity due to short-circuit evaluation, preferring approaches like mul-then-div overflow probes that avoid conditional jump overhead.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-08-27T13:37:22.601Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol:126-133
Timestamp: 2025-08-27T13:37:22.601Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister prefers to avoid inline `if` statements (even if they were supported in Solidity) because they create fragile code with meaningful whitespace and make debugging difficult when console logs need to be added, potentially causing subtle behavior changes if braces aren't reintroduced properly.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-11T14:32:50.439Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/implementation/LibDecimalFloatImplementation.maximize.t.sol:15-29
Timestamp: 2025-08-11T14:32:50.439Z
Learning: In test code for the rain.math.float repository, redundant checks may be intentionally kept for clarity and documentation purposes, even when they could be simplified. The maintainer (thedavidmeister) prefers explicit assertions in test code to make the test's intent clear to future readers, prioritizing readability over conciseness.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-06-17T10:17:56.205Z
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.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T15:50:31.262Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#110
File: src/lib/implementation/LibDecimalFloatImplementation.sol:1011-1011
Timestamp: 2025-08-26T15:50:31.262Z
Learning: In the rain.math.float repository, thedavidmeister prefers concise, high-level documentation comments that capture intent rather than detailed comments that mirror implementation specifics. Detailed implementation-focused comments are considered fragile because they become outdated when code changes but comments aren't updated.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-07-03T11:20:50.456Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#70
File: crates/float/src/evm.rs:38-43
Timestamp: 2025-07-03T11:20:50.456Z
Learning: In the rainlanguage/rain.math.float codebase, the user 0xgleb prefers not to add explanatory comments for well-established Rust idioms like the double `?` pattern, as these are self-explanatory to experienced Rust developers and don't need over-commenting.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.sol
📚 Learning: 2025-09-01T18:59:30.642Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:99-110
Timestamp: 2025-09-01T18:59:30.642Z
Learning: In Solidity, you cannot cast arrays to strings due to type system limitations. String concatenation optimizations using bytes arrays and casting aren't viable because of fundamental differences in how Solidity handles these data types - strings consist of individual bytes while arrays contain 32-byte integers, making direct casting impossible.

Applied to files:

  • src/lib/format/LibFormatDecimalFloat.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:

  • src/lib/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
  • test/src/concrete/DecimalFloat.format.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: 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/format/LibFormatDecimalFloat.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T16:50:11.113Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/LibDecimalFloat.div.t.sol:48-51
Timestamp: 2025-08-26T16:50:11.113Z
Learning: In LibDecimalFloat, packLossless internally asserts that the packing operation is lossless, so there's no need to manually check the lossless flag when using packLossless instead of packLossy.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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 (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: rainix (macos-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: rainix (macos-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: git-clean
🔇 Additional comments (2)
test/src/concrete/DecimalFloat.format.t.sol (1)

13-15: LGTM: test helper correctly mirrors new API.

The helper passes through sigFigsLimit to the library as expected.

test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1)

33-33: Simplify cast noise in bound().

No need for the intermediate int256 cast.

- value = bound(value, 0, uint256(int256(type(int224).max)));+ value = bound(value, 0, uint256(type(int224).max));
⛔ Skipped due to learnings
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#115
File: test/src/lib/LibDecimalFloat.decimal.t.sol:56-56
Timestamp: 2025-08-29T15:26:25.984Z
Learning: In Forge tests, the bound() function is overloaded and requires explicit type casting for overload resolution. When using bound(value, min, max), if the max parameter involves type conversions, an outer cast on the entire bound() call may be necessary for the compiler to resolve which overload to use. Additionally, Solidity cannot change both signedness and bit width in a single cast, so complex casts like uint256(int256(type(int224).max)) require the staged approach rather than a direct cast.
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.

Comment threadsrc/concrete/DecimalFloat.sol
Comment threadsrc/lib/format/LibFormatDecimalFloat.sol
Comment threadtest/src/concrete/DecimalFloat.format.t.sol

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 1

♻️ Duplicate comments (4)
test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol (2)

99-103: Fix misleading comment about trailing zeros after decimal.

Align the comment with the asserted expectations.

- // trailing zeros after decimal are not significant- // 1.00 and 0.00100+ // trailing zeros after decimal (as represented here):+ // 1.00 => 1; 0.00100 => 3
checkCountSigFigs(100, -2, 1);
checkCountSigFigs(100, -5, 3);

117-121: Briefly document the [-76, 0] bound rationale.

Adds future-proof context for the safe power computation and matches maximize/log10 edges.

 function testCountSigFigsOne(int256 exponent) external pure {
- exponent = bound(exponent, -76, 0);+ // Bound to [-76, 0] so 10**(-exponent) <= 1e76 fits in int256 (10**77 would overflow).+ // This also matches maximize/log10 edge behavior around 1e76.+ exponent = bound(exponent, -76, 0);
int256 one = int256(10 ** uint256(-exponent));
checkCountSigFigs(one, exponent, 1);
checkCountSigFigs(-one, exponent, 1);
}
test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (2)

27-38: Bound fuzzed sigFigsLimit to a sane range.

Prevents 0/oversized limits from skewing expectations; test a separate case for 0 if needed.

 function testFormatDecimalRoundTripNonNegative(uint256 value, uint256 sigFigsLimit) external pure {
value = bound(value, 0, uint256(int256(type(int224).max)));
+ sigFigsLimit = bound(sigFigsLimit, 1, 76);
Float float = LibDecimalFloat.fromFixedDecimalLosslessPacked(value, 18);
string memory formatted = LibFormatDecimalFloat.toDecimalString(float, sigFigsLimit);
(bytes4 errorCode, Float parsed) = LibParseDecimalFloat.parseDecimalFloat(formatted);
assertEq(errorCode, 0, "Parse error");
assertTrue(float.eq(parsed), "Round trip failed");
// Canonicalization: format(parse(format(x))) == format(x)
string memory reFormatted = LibFormatDecimalFloat.toDecimalString(parsed, sigFigsLimit);
assertEq(formatted, reFormatted, "Formatting not canonical");
}

165-170: Correct comments: formatter does not round.

Update wording to match project behavior; expectation remains unchanged.

- // Force rounding under a tighter sig-figs limit.+ // No rounding: sigFigsLimit only selects when to use scientific notation.
Float f = LibDecimalFloat.packLossless(12345678, 0);
string memory s = LibFormatDecimalFloat.toDecimalString(f, 5);
- // Verify the explicit limit path (adjust expected if rounding policy differs).+ // Verify the explicit limit path.
assertEq(s, "1.2345678e7", "Custom sig-figs not applied");
📜 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 6455a0a and 5faaae4.

📒 Files selected for processing (2)
  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol (1 hunks)
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (21)
📓 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#119
File: test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol:174-175
Timestamp: 2025-09-02T09:33:32.485Z
Learning: The LibFormatDecimalFloat.toDecimalString function in src/lib/format/LibFormatDecimalFloat.sol does not include rounding logic. It formats decimal floats as-is without rounding values based on significant figures limits.
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.
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.
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-09-02T09:33:32.485Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol:174-175
Timestamp: 2025-09-02T09:33:32.485Z
Learning: The LibFormatDecimalFloat.toDecimalString function in src/lib/format/LibFormatDecimalFloat.sol does not include rounding logic. It formats decimal floats as-is without rounding values based on significant figures limits.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
📚 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/format/LibFormatDecimalFloat.countSigFigs.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: 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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-11T14:32:50.439Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/implementation/LibDecimalFloatImplementation.maximize.t.sol:15-29
Timestamp: 2025-08-11T14:32:50.439Z
Learning: In test code for the rain.math.float repository, redundant checks may be intentionally kept for clarity and documentation purposes, even when they could be simplified. The maintainer (thedavidmeister) prefers explicit assertions in test code to make the test's intent clear to future readers, prioritizing readability over conciseness.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.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:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-06-17T10:17:56.205Z
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.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T16:50:11.113Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/LibDecimalFloat.div.t.sol:48-51
Timestamp: 2025-08-26T16:50:11.113Z
Learning: In LibDecimalFloat, packLossless internally asserts that the packing operation is lossless, so there's no need to manually check the lossless flag when using packLossless instead of packLossy.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-27T13:37:22.601Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol:126-133
Timestamp: 2025-08-27T13:37:22.601Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister prefers to avoid inline `if` statements (even if they were supported in Solidity) because they create fragile code with meaningful whitespace and make debugging difficult when console logs need to be added, potentially causing subtle behavior changes if braces aren't reintroduced properly.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T15:50:31.262Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#110
File: src/lib/implementation/LibDecimalFloatImplementation.sol:1011-1011
Timestamp: 2025-08-26T15:50:31.262Z
Learning: In the rain.math.float repository, thedavidmeister prefers concise, high-level documentation comments that capture intent rather than detailed comments that mirror implementation specifics. Detailed implementation-focused comments are considered fragile because they become outdated when code changes but comments aren't updated.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-09-02T09:40:19.150Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol:0-0
Timestamp: 2025-09-02T09:40:19.150Z
Learning: Solidity signed integers do not have a separate representation for negative zero (like IEEE 754 floating point numbers). There is no concept of -0 being distinct from 0 in Solidity's type system.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T12:49:02.313Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#107
File: src/lib/implementation/LibDecimalFloatImplementation.sol:282-289
Timestamp: 2025-08-26T12:49:02.313Z
Learning: In Solidity 0.8+, arithmetic operations outside unchecked blocks automatically revert on overflow/underflow, so manual overflow checks are redundant and should be avoided for gas efficiency.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-07-03T11:20:50.456Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#70
File: crates/float/src/evm.rs:38-43
Timestamp: 2025-07-03T11:20:50.456Z
Learning: In the rainlanguage/rain.math.float codebase, the user 0xgleb prefers not to add explanatory comments for well-established Rust idioms like the double `?` pattern, as these are self-explanatory to experienced Rust developers and don't need over-commenting.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-14T16:56:28.978Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#99
File: src/lib/implementation/LibDecimalFloatImplementation.sol:574-581
Timestamp: 2025-08-14T16:56:28.978Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister avoids using `&&` operators in gas-critical paths because they involve jumps in Solidity due to short-circuit evaluation, preferring approaches like mul-then-div overflow probes that avoid conditional jump overhead.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-09-01T19:11:36.614Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:0-0
Timestamp: 2025-09-01T19:11:36.614Z
Learning: In the rainlanguage/rain.math.float project, the codebase targets a specific OpenZeppelin version rather than maintaining compatibility across arbitrary OZ versions. Direct usage of Strings.toString on signed integers is acceptable within this versioning approach.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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, rainix-rs-test)
  • 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, test-wasm-build)
  • GitHub Check: rainix (macos-latest, rainix-sol-legal)
🔇 Additional comments (2)
test/src/lib/format/LibFormatDecimalFloat.countSigFigs.t.sol (1)

16-111: Good breadth and symmetry of examples.

Covers ±coefficients, shifting via exponent, and positive-exponent growth; reads well.

test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1)

55-162: Example matrix looks solid.

Nice coverage of zeros, decimals, ±, 9→10 sig-fig cutoff, and extreme magnitudes.

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 1

♻️ Duplicate comments (2)
test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (2)

170-175: Fix misleading comment: no rounding is performed by toDecimalString.

The formatter switches notation based on sigFigsLimit; it does not round/truncate digits.

- // Force rounding under a tighter sig-figs limit.+ // No rounding: sigFigsLimit only controls when we switch to scientific notation.

28-38: Bound fuzzed sigFigsLimit to a sane range (avoid 0/huge values).

Unbounded, the fuzz can pass 0 or very large limits, which isn’t meaningful for the formatter and can create noisy cases. Clamp to a practical max (76 matches your extreme exponent coverage).

 function testFormatDecimalRoundTripNonNegative(uint256 value, uint256 sigFigsLimit) external pure {
value = bound(value, 0, uint256(int256(type(int224).max)));
+ sigFigsLimit = bound(sigFigsLimit, 1, 76);
Float float = LibDecimalFloat.fromFixedDecimalLosslessPacked(value, 18);
string memory formatted = LibFormatDecimalFloat.toDecimalString(float, sigFigsLimit);
 function testFormatDecimalRoundTripNegative(int256 value, uint256 sigFigsLimit) external pure {
value = bound(value, 1, int256(type(int128).max));
+ sigFigsLimit = bound(sigFigsLimit, 1, 76);
Float float = LibDecimalFloat.fromFixedDecimalLosslessPacked(uint256(value), 18);
string memory formatted = float.toDecimalString(sigFigsLimit);

Also applies to: 41-47

📜 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 5faaae4 and 2224def.

📒 Files selected for processing (1)
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (19)
📓 Common learnings
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol:174-175
Timestamp: 2025-09-02T09:33:32.485Z
Learning: The LibFormatDecimalFloat.toDecimalString function in src/lib/format/LibFormatDecimalFloat.sol does not include rounding logic. It formats decimal floats as-is without rounding values based on significant figures limits.
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.
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.
📚 Learning: 2025-09-02T09:33:32.485Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol:174-175
Timestamp: 2025-09-02T09:33:32.485Z
Learning: The LibFormatDecimalFloat.toDecimalString function in src/lib/format/LibFormatDecimalFloat.sol does not include rounding logic. It formats decimal floats as-is without rounding values based on significant figures limits.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T16:50:11.113Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/LibDecimalFloat.div.t.sol:48-51
Timestamp: 2025-08-26T16:50:11.113Z
Learning: In LibDecimalFloat, packLossless internally asserts that the packing operation is lossless, so there's no need to manually check the lossless flag when using packLossless instead of packLossy.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-11T14:32:50.439Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/implementation/LibDecimalFloatImplementation.maximize.t.sol:15-29
Timestamp: 2025-08-11T14:32:50.439Z
Learning: In test code for the rain.math.float repository, redundant checks may be intentionally kept for clarity and documentation purposes, even when they could be simplified. The maintainer (thedavidmeister) prefers explicit assertions in test code to make the test's intent clear to future readers, prioritizing readability over conciseness.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-06-17T10:17:56.205Z
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.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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: 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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-27T13:37:22.601Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol:126-133
Timestamp: 2025-08-27T13:37:22.601Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister prefers to avoid inline `if` statements (even if they were supported in Solidity) because they create fragile code with meaningful whitespace and make debugging difficult when console logs need to be added, potentially causing subtle behavior changes if braces aren't reintroduced properly.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T15:50:31.262Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#110
File: src/lib/implementation/LibDecimalFloatImplementation.sol:1011-1011
Timestamp: 2025-08-26T15:50:31.262Z
Learning: In the rain.math.float repository, thedavidmeister prefers concise, high-level documentation comments that capture intent rather than detailed comments that mirror implementation specifics. Detailed implementation-focused comments are considered fragile because they become outdated when code changes but comments aren't updated.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-09-02T09:40:19.150Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol:0-0
Timestamp: 2025-09-02T09:40:19.150Z
Learning: Solidity signed integers do not have a separate representation for negative zero (like IEEE 754 floating point numbers). There is no concept of -0 being distinct from 0 in Solidity's type system.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T12:49:02.313Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#107
File: src/lib/implementation/LibDecimalFloatImplementation.sol:282-289
Timestamp: 2025-08-26T12:49:02.313Z
Learning: In Solidity 0.8+, arithmetic operations outside unchecked blocks automatically revert on overflow/underflow, so manual overflow checks are redundant and should be avoided for gas efficiency.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-07-03T11:20:50.456Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#70
File: crates/float/src/evm.rs:38-43
Timestamp: 2025-07-03T11:20:50.456Z
Learning: In the rainlanguage/rain.math.float codebase, the user 0xgleb prefers not to add explanatory comments for well-established Rust idioms like the double `?` pattern, as these are self-explanatory to experienced Rust developers and don't need over-commenting.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-14T16:56:28.978Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#99
File: src/lib/implementation/LibDecimalFloatImplementation.sol:574-581
Timestamp: 2025-08-14T16:56:28.978Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister avoids using `&&` operators in gas-critical paths because they involve jumps in Solidity due to short-circuit evaluation, preferring approaches like mul-then-div overflow probes that avoid conditional jump overhead.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-09-01T19:11:36.614Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:0-0
Timestamp: 2025-09-01T19:11:36.614Z
Learning: In the rainlanguage/rain.math.float project, the codebase targets a specific OpenZeppelin version rather than maintaining compatibility across arbitrary OZ versions. Direct usage of Strings.toString on signed integers is acceptable within this versioning approach.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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 (macos-latest, rainix-rs-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-static)
  • GitHub Check: rainix (macos-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-static)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, rainix-rs-test)
  • GitHub Check: git-clean
🔇 Additional comments (2)
test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (2)

56-168: Examples suite looks comprehensive and correct at boundaries.

Good coverage of 9→10 sig-figs cutoff, pure powers of ten, and ±76 exponent limits.


17-25: Helper is clear and minimal.

Packing + single assert keeps examples concise and readable.

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 2

♻️ Duplicate comments (1)
test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1)

173-179: Fix misleading comment: formatter doesn’t round.

Aligns with project behavior (sigFigsLimit only gates notation).

Apply:

- // Force rounding under a tighter sig-figs limit.+ // No rounding: sigFigsLimit only controls notation (scientific vs plain).
📜 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 2224def and c47e56f.

📒 Files selected for processing (2)
  • .gas-snapshot (8 hunks)
  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (20)
📓 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#119
File: test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol:174-175
Timestamp: 2025-09-02T09:33:32.485Z
Learning: The LibFormatDecimalFloat.toDecimalString function in src/lib/format/LibFormatDecimalFloat.sol does not include rounding logic. It formats decimal floats as-is without rounding values based on significant figures limits.
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.
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.
📚 Learning: 2025-09-02T09:33:32.485Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol:174-175
Timestamp: 2025-09-02T09:33:32.485Z
Learning: The LibFormatDecimalFloat.toDecimalString function in src/lib/format/LibFormatDecimalFloat.sol does not include rounding logic. It formats decimal floats as-is without rounding values based on significant figures limits.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
  • .gas-snapshot
📚 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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-06-16T13:17:28.513Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#58
File: src/concrete/DecimalFloat.sol:175-182
Timestamp: 2025-06-16T13:17:28.513Z
Learning: In the rainlanguage/rain.math.float codebase, there's an established naming convention where functions accepting a `Float` type parameter consistently use `float` as the parameter name, even though it shadows the type name. This pattern is used throughout `LibDecimalFloat.sol` and should be maintained for consistency in related contracts like `DecimalFloat.sol`.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T16:50:11.113Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/LibDecimalFloat.div.t.sol:48-51
Timestamp: 2025-08-26T16:50:11.113Z
Learning: In LibDecimalFloat, packLossless internally asserts that the packing operation is lossless, so there's no need to manually check the lossless flag when using packLossless instead of packLossy.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-11T14:32:50.439Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#96
File: test/src/lib/implementation/LibDecimalFloatImplementation.maximize.t.sol:15-29
Timestamp: 2025-08-11T14:32:50.439Z
Learning: In test code for the rain.math.float repository, redundant checks may be intentionally kept for clarity and documentation purposes, even when they could be simplified. The maintainer (thedavidmeister) prefers explicit assertions in test code to make the test's intent clear to future readers, prioritizing readability over conciseness.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-06-17T10:17:56.205Z
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.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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: 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:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
  • .gas-snapshot
📚 Learning: 2025-08-27T13:37:22.601Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#111
File: test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol:126-133
Timestamp: 2025-08-27T13:37:22.601Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister prefers to avoid inline `if` statements (even if they were supported in Solidity) because they create fragile code with meaningful whitespace and make debugging difficult when console logs need to be added, potentially causing subtle behavior changes if braces aren't reintroduced properly.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T15:50:31.262Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#110
File: src/lib/implementation/LibDecimalFloatImplementation.sol:1011-1011
Timestamp: 2025-08-26T15:50:31.262Z
Learning: In the rain.math.float repository, thedavidmeister prefers concise, high-level documentation comments that capture intent rather than detailed comments that mirror implementation specifics. Detailed implementation-focused comments are considered fragile because they become outdated when code changes but comments aren't updated.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-09-02T09:40:19.150Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol:0-0
Timestamp: 2025-09-02T09:40:19.150Z
Learning: Solidity signed integers do not have a separate representation for negative zero (like IEEE 754 floating point numbers). There is no concept of -0 being distinct from 0 in Solidity's type system.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-26T12:49:02.313Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#107
File: src/lib/implementation/LibDecimalFloatImplementation.sol:282-289
Timestamp: 2025-08-26T12:49:02.313Z
Learning: In Solidity 0.8+, arithmetic operations outside unchecked blocks automatically revert on overflow/underflow, so manual overflow checks are redundant and should be avoided for gas efficiency.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-07-03T11:20:50.456Z
Learnt from: 0xgleb
PR: rainlanguage/rain.math.float#70
File: crates/float/src/evm.rs:38-43
Timestamp: 2025-07-03T11:20:50.456Z
Learning: In the rainlanguage/rain.math.float codebase, the user 0xgleb prefers not to add explanatory comments for well-established Rust idioms like the double `?` pattern, as these are self-explanatory to experienced Rust developers and don't need over-commenting.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-08-14T16:56:28.978Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#99
File: src/lib/implementation/LibDecimalFloatImplementation.sol:574-581
Timestamp: 2025-08-14T16:56:28.978Z
Learning: In the rainlanguage/rain.math.float repository, maintainer thedavidmeister avoids using `&&` operators in gas-critical paths because they involve jumps in Solidity due to short-circuit evaluation, preferring approaches like mul-then-div overflow probes that avoid conditional jump overhead.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol
📚 Learning: 2025-09-01T19:11:36.614Z
Learnt from: thedavidmeister
PR: rainlanguage/rain.math.float#119
File: src/lib/format/LibFormatDecimalFloat.sol:0-0
Timestamp: 2025-09-01T19:11:36.614Z
Learning: In the rainlanguage/rain.math.float project, the codebase targets a specific OpenZeppelin version rather than maintaining compatibility across arbitrary OZ versions. Direct usage of Strings.toString on signed integers is acceptable within this versioning approach.

Applied to files:

  • test/src/lib/format/LibFormatDecimalFloat.toDecimalString.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:

  • .gas-snapshot
⏰ 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-static)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-test)
  • GitHub Check: rainix (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: rainix (ubuntu-latest, test-wasm-build)
  • 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 (macos-latest, rainix-rs-test)
  • GitHub Check: git-clean
🔇 Additional comments (5)
.gas-snapshot (4)

1-325: Re-baselining acknowledged.

Suite-wide shifts look consistent with formatter/parse changes; keeping snapshot in sync is fine.


13-13: Slight gas uptick on format deployed; expected.

Formatting now takes sigFigsLimit; a minor increase here is reasonable.


274-282: Pow/Pow10 deltas are sizable; double-check expectations.

If driven by shared code size effects, fine; otherwise investigate.


230-230: No significant gas delta for log10 packed The measured change for LibDecimalFloatLog10Test:testLog10Packed is only –89 μs (1,646,491 μs → 1,646,402 μs), well within normal benchmark variance.

Likely an incorrect or invalid review comment.

test/src/lib/format/LibFormatDecimalFloat.toDecimalString.t.sol (1)

157-165: Good boundary coverage (cutoff powers and ±76 exponents).

These examples nail the notation-switch and magnitude edges.

@thedavidmeister
thedavidmeister merged commit fcffed5 into mainSep 2, 2025
10 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

Tip

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

SIZE=L


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

@coderabbitaicoderabbitaiBot mentioned this pull request Oct 2, 2025
4 tasks
@coderabbitaicoderabbitaiBot mentioned this pull request Mar 23, 2026
4 tasks
Sign up for freeto 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

@thedavidmeister