Skip to content

feat: add PercentageFloat method for decimal percentage support#167

Open
walle250ai wants to merge 2 commits into
Rhymond:masterfrom
walle250ai:feature/add-percentage-float-method
Open

feat: add PercentageFloat method for decimal percentage support#167
walle250ai wants to merge 2 commits into
Rhymond:masterfrom
walle250ai:feature/add-percentage-float-method

Conversation

@walle250ai

@walle250ai walle250ai commented Apr 29, 2026

Copy link
Copy Markdown

Problem

Percentage(int64) only supports whole-number rates. Real-world tax and discount calculations frequently require decimal percentages (12.5% VAT, 6.25% sales tax, etc.) that cannot be represented.

Solution

Adds PercentageFloat(percentage float64) (*Money, error) that accepts a float64 rate in [0.0, 100.0] while keeping all arithmetic in integer space.

Implementation approach

Instead of multiplying amount by a float, the method scales the percentage to a large integer numerator/denominator pair (precision = 1e9) and delegates to the existing calculator.allocate(amount, numerator, denominator) path:

const precision = 1e9
numerator  := int64(percentage * precision)   // e.g. 12.5 → 12_500_000_000
denominator := int64(100 * precision)          // always 100_000_000_000

This avoids floating-point amounts entirely, consistent with the library's design.

Truncation note: results are truncated towards zero (e.g. 33.3% of $3.00 → $0.99, not $1.00). This matches the behaviour of Allocate and Split.

Tests

Case Input Expected
12.5% of $200 PercentageFloat(12.5) on 200 25
6.25% of $800 PercentageFloat(6.25) on 800 50
33.3% of $300 (truncation) PercentageFloat(33.3) on 300 99
Boundary 0.0% PercentageFloat(0.0) 0
Boundary 100.0% PercentageFloat(100.0) 100
Invalid -0.1 error
Invalid 100.1 error

All existing tests continue to pass.

🤖 Generated with Claude Code

Summary by Sourcery

Add integer- and float-based percentage calculation helpers to Money with validation and coverage tests.

New Features:

  • Introduce Money.Percentage to compute whole-number percentage values of a Money amount with range validation.
  • Introduce Money.PercentageFloat to compute decimal percentage values of a Money amount using high-precision integer arithmetic and truncation toward zero.

Tests:

  • Add unit tests for Money.Percentage, including normal, boundary, and invalid percentage cases.
  • Add unit tests for Money.PercentageFloat, including fractional, negative-amount, boundary, and invalid percentage cases.

walle250ai and others added 2 commits April 29, 2026 22:24
Adds Money.Percentage(percentage int64) (*Money, error) that returns a new
Money representing the given integer percentage of the original amount.
Uses the existing calculator.allocate path (amount*p/100) for consistency
with Allocate/Split. Returns an error for percentages outside [0, 100].
Tests cover normal cases, boundary values, and invalid inputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Percentage(int64) only handles whole-number rates; real-world tax and
discount rates like 12.5% or 6.25% require decimal support.

PercentageFloat(float64) converts the rate to a scaled numerator/denominator
(precision 1e9) and delegates to the existing calculator.allocate path so all
arithmetic stays in integer space. Returns an error for rates outside [0, 100].

Tests cover 12.5% of $200→$25, 6.25% of $800→$50, 33.3% of $300→$99
(documented truncation), boundary values, and invalid inputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Apr 29, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds percentage-based calculation helpers to Money, including a new float-based API that keeps arithmetic in integer space, with comprehensive validation and tests for both integer and float percentages.

Sequence diagram for PercentageFloat delegation to calculator

sequenceDiagram
    actor Caller
    participant Money
    participant mutate
    participant calc

    Caller ->> Money: PercentageFloat(percentage float64)
    Money ->> Money: validate percentage in [0.0, 100.0]
    Money ->> Money: compute precision, numerator, denominator
    Money ->> mutate: use calc to allocate amount
    mutate ->> calc: allocate(m.amount, numerator, denominator)
    calc -->> mutate: allocatedAmount int64
    mutate -->> Money: allocatedAmount int64
    Money ->> Money: construct new Money(allocatedAmount, m.currency)
    Money -->> Caller: *Money, nil
Loading

Class diagram for Money with new percentage helpers

classDiagram
    class Money {
        int64 amount
        Currency currency
        Allocate(rs ...int) []*Money, error
        Percentage(percentage int64) *Money, error
        PercentageFloat(percentage float64) *Money, error
        Display() string
    }

    class Mutate {
        Calculator calc
    }

    class Calculator {
        allocate(amount int64, numerator int64, denominator int64) int64
    }

    Money --> Mutate : uses mutate
    Mutate --> Calculator : uses calc

    %% New methods introduced in this PR
    Money : Percentage(percentage int64) *Money, error
    Money : PercentageFloat(percentage float64) *Money, error
Loading

File-Level Changes

Change Details Files
Introduce integer-based Percentage helper on Money with input validation
  • Add Money.Percentage(int64) method that validates the rate is between 0 and 100 inclusive
  • Implement percentage calculation by delegating to mutate.calc.allocate with numerator=percentage and denominator=100
  • Return a typed error message on invalid input and construct a new Money with the computed amount and original currency
money.go
Add PercentageFloat helper that accepts decimal percentages while keeping integer arithmetic
  • Add Money.PercentageFloat(float64) with validation for range [0.0, 100.0] and shared error message format
  • Convert float percentage into a scaled integer fraction using precision 1e9 and call mutate.calc.allocate(amount, numerator, denominator)
  • Document truncation-towards-zero behaviour for non-integer results and preserve original currency in the returned Money
money.go
Add unit tests covering normal, boundary, invalid, and truncation scenarios for Percentage and PercentageFloat
  • Introduce TestMoney_Percentage, TestMoney_Percentage_BoundaryValues, and TestMoney_Percentage_InvalidInput to cover valid, boundary, and out-of-range integer percentages
  • Introduce TestMoney_PercentageFloat and TestMoney_PercentageFloat_InvalidInput to validate decimal percentages, truncation behaviour, negative amounts, and error cases
  • Verify returned error messages match the expected string and that invalid inputs yield nil Money results
money_test.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • In PercentageFloat, consider rounding the scaled percentage (e.g., int64(math.Round(percentage * precision))) instead of truncating the float64 multiplication result to reduce the impact of floating-point representation error on boundary cases.
  • The range checks and error messages for Percentage and PercentageFloat are duplicated; extracting a shared validation/helper or a shared error value would keep behavior consistent and simplify future changes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `PercentageFloat`, consider rounding the scaled percentage (e.g., `int64(math.Round(percentage * precision))`) instead of truncating the float64 multiplication result to reduce the impact of floating-point representation error on boundary cases.
- The range checks and error messages for `Percentage` and `PercentageFloat` are duplicated; extracting a shared validation/helper or a shared error value would keep behavior consistent and simplify future changes.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant