Skip to content

Fixed point sqrt#286

Open
immrsd wants to merge 14 commits intomainfrom
fixed-point-sqrt
Open

Fixed point sqrt#286
immrsd wants to merge 14 commits intomainfrom
fixed-point-sqrt

Conversation

@immrsd
Copy link
Copy Markdown
Collaborator

@immrsd immrsd commented Apr 1, 2026

Resolves #90

Summary by CodeRabbit

Release Notes

  • New Features

    • Added square root operations for fixed-point number types
    • Extended API documentation for fixed-point arithmetic operations and casting routes
  • Tests

    • Added comprehensive test coverage for square root functionality

@immrsd immrsd self-assigned this Apr 1, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 1, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1ee2095e-83d3-4fa5-b617-b261b6b61933

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Added square root functionality to the fixed-point math library by introducing a shared fp_helpers module containing a sqrt_floor helper function implementing Newton's method, implementing sqrt for both SD29x9 and UD30x9 types, updating API documentation, and providing comprehensive test suites for both implementations.

Changes

Cohort / File(s) Summary
Documentation
math/fixed_point/README.md
Expanded API documentation to list additional arithmetic operations (abs, ceil, div, floor, mul, pow, sqrt) and added new casting/helper routes between types and constructor methods.
Core Implementation
math/fixed_point/sources/fp_helpers.move, math/fixed_point/sources/sd29x9/sd29x9_base.move, math/fixed_point/sources/ud30x9/ud30x9_base.move
Introduced shared sqrt_floor(a: u256) helper using Newton's method for floor square root computation, implemented sqrt(x: SD29x9) with sign validation and magnitude scaling, and implemented sqrt(x: UD30x9) with raw value unwrapping and re-wrapping.
Public API Re-exports
math/fixed_point/sources/sd29x9/sd29x9.move, math/fixed_point/sources/ud30x9/ud30x9.move
Added public re-exports mapping sqrt functions from base modules to top-level type namespaces for API consistency.
Test Coverage
math/fixed_point/tests/sd29x9_tests/sqrt_tests.move, math/fixed_point/tests/ud30x9_tests/sqrt_tests.move
Added comprehensive test suites covering zero/one/perfect squares, fractional inputs, irrational truncation, floor property verification, roundtrip correctness, edge cases (max/min values), and monotonicity for both types; SD29x9 tests also verify negative input rejection.

Sequence Diagram(s)

sequenceDiagram
    participant UD30x9 as UD30x9::sqrt()
    participant SD29x9 as SD29x9::sqrt()
    participant Helper as fp_helpers::sqrt_floor()
    participant Newton as Newton Iteration
    
    rect rgba(100, 150, 255, 0.5)
    UD30x9->>Helper: sqrt_floor(raw * SCALE)
    end
    
    rect rgba(100, 200, 100, 0.5)
    SD29x9->>Helper: sqrt_floor(magnitude * SCALE)
    end
    
    rect rgba(200, 100, 100, 0.5)
    Helper->>Newton: Initial approximation + iterations
    Newton->>Newton: (xn + a/xn) >> 1
    Newton->>Helper: Refined result
    end
    
    Helper-->>UD30x9: floor(sqrt(a))
    Helper-->>SD29x9: floor(sqrt(a))
    UD30x9->>UD30x9: wrap(result)
    SD29x9->>SD29x9: wrap(result as SD29x9)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • 0xNeshi
  • bidzyyys
  • ericnordelo

Poem

🐰 A rabbit hops through numbered fields so bright,
Where square roots bloom with Newton's guiding light,
From sqrt_floor to SD29x9,
Fixed-point math now feels so divine,
Tests and helpers dancing—oh what a sight! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description only contains 'Resolves #90' and is missing required checklist items (Tests, Documentation, Changelog) that should be marked as complete or pending. Complete the PR description by adding the checklist with status updates for Tests, Documentation, and Changelog items to indicate what was done.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fixed point sqrt' is concise and clearly describes the main change: implementing square root functionality for fixed-point types.
Linked Issues check ✅ Passed The PR successfully implements square root for fixed-point math, as requested in issue #90. Multiple commits add sqrt helper functions, implementations for SD29x9 and UD30x9, comprehensive tests, and documentation updates.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing sqrt functionality: helper functions, implementations for both fixed-point types, tests, and README documentation. No out-of-scope changes detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fixed-point-sqrt

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 1, 2026

Codecov Report

❌ Patch coverage is 97.82609% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 90.49%. Comparing base (0a996c9) to head (53fd0bd).

Files with missing lines Patch % Lines
math/fixed_point/sources/internal/common.move 97.05% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #286      +/-   ##
==========================================
+ Coverage   90.32%   90.49%   +0.16%     
==========================================
  Files          21       21              
  Lines        1882     1925      +43     
  Branches      521      538      +17     
==========================================
+ Hits         1700     1742      +42     
  Misses        160      160              
- Partials       22       23       +1     
Flag Coverage Δ
contracts/access 44.33% <ø> (ø)
math/core 86.30% <ø> (ø)
math/fixed_point 66.61% <97.82%> (+2.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

public fun sqrt(x: UD30x9): UD30x9 {
let raw = x.unwrap() as u256;
if (raw == 0) {
return wrap(0)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Use zero()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Shouldn't be handled in fp_helpers?

if (mag == 0) {
return zero()
};
let result = fp_helpers::sqrt_floor(mag * SCALE);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Add comment explaining why * SCALE is necessary

if (raw == 0) {
return wrap(0)
};
let result = fp_helpers::sqrt_floor(raw * SCALE_U256);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Add comment explaining why * SCALE_U256 is necessary

public fun sqrt(x: SD29x9): SD29x9 {
let Components { neg, mag } = decompose(x.unwrap());
assert!(!neg, ENegativeSqrt);
if (mag == 0) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Zero-check is redundant, as sqrt_floor performs it in the first line


#[test]
fun sqrt_of_zero_is_zero() {
expect(fixed(0).sqrt(), fixed(0));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
expect(fixed(0).sqrt(), fixed(0));
expect(ud30x9::zero().sqrt(), ud30x9::zero());

values.destroy!(|x| {
let result = x.sqrt();
// Result is non-negative: raw bits should not have sign bit set
assert!(result.unwrap() <= 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF);
Copy link
Copy Markdown
Collaborator

@0xNeshi 0xNeshi Apr 1, 2026

Choose a reason for hiding this comment

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

It would be useful to have something like sd29x9::is_positive & sd29x9::is_negative, so this assertion would become:

assert!(result.is_positive());

Comment on lines +77 to +78
#[test]
fun sqrt_result_is_always_non_negative() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
#[test]
fun sqrt_result_is_always_non_negative() {
#[random_test]
fun sqrt_result_is_always_non_negative(raw: u128) {

Perfect candidate for a prop test

Comment on lines +62 to +63
assert!(r * r <= scaled);
assert!((r + 1) * (r + 1) > scaled);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

These assertions are invariants on all non-negative values, making this a good candidate for a proptest.

Alternatively, these invariant assertions could be incorporated into sqrt_result_is_always_non_negative.

Comment on lines +95 to +96
#[test]
fun sqrt_floor_property_for_non_perfect_squares() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  • these invariants hold for all valid values, not just non-perfect squares
  • therefore, candidate for prop. test

Comment on lines +142 to +143
#[test]
fun sqrt_monotonicity() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
#[test]
fun sqrt_monotonicity() {
#[random_test]
fun sqrt_monotonicity(x: u128, y: u128) {

good candidate for a prop. test

Copy link
Copy Markdown
Collaborator

@bidzyyys bidzyyys left a comment

Choose a reason for hiding this comment

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

Left some comments. @immrsd resolve conflicts first.

public fun sqrt(x: UD30x9): UD30x9 {
let raw = x.unwrap() as u256;
if (raw == 0) {
return wrap(0)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Shouldn't be handled in fp_helpers?

///
/// #### Returns
/// - `floor(sqrt(a))`.
public(package) fun sqrt_floor(a: u256): u256 {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Shouldn't we use implementation from openzeppelin_math? I feel like it looks like code duplication.
Curious to hear your thoughts @ericnordelo & @0xNeshi 👀

Copy link
Copy Markdown
Collaborator

@0xNeshi 0xNeshi Apr 27, 2026

Choose a reason for hiding this comment

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

That would require importing openzeppelin_math into openzeppelin_fp_math. If the final package size isn't significantly increased, i.e. the Move compiler prunes all unused openzeppelin_mathtypes/functions from the finalopenzeppelin_fp_math` package, I think this is the right approach.

Otherwise, it might be better to duplicate code to avoid unnecessary package size increase.

@ericnordelo do we know Move compiler's behavior in this case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: sqrt for fixed-point math lib

3 participants