Skip to content

Keep the remainder of % smaller than the divisor [patch] - #111

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/mod-remainder-equals-divisor
Sep 28, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/mod-remainder-equals-divisor

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #109

What was wrong

Mod rounded PreciseNumber.Mod(left, right) to the operands' fewest significant digits. A remainder is usually much smaller than the operands, so that rounding could carry it up to the divisor itself: 19.9 % 2 == 2, 9.96 % 5 == 5, -19.9 % 2 == -2. That breaks the |x % n| < |n| invariant.

Fix

  • A remainder is a subtraction (left - right × quotient), so round it to the fewest decimal places, the way Subtract does, using the existing LowestDecimalDigits helper.
  • The decimal-places rule alone does not restore the invariant. 1.9 rounded to 0 places is still 2. And when the divisor has more decimals than the dividend, rounding can land past it: 6.8 % 2.27 is exactly 2.26, which rounds to 2.3. So when the rounded magnitude reaches |right|, the result is zero. At that precision, the remainder is indistinguishable from a whole multiple of the divisor.
  • The README feature list and the class remarks said modulus followed the significant-digits rule. They now say it follows the decimal-places rule.

This touches only Mod and the remarks. It does not conflict with #110 (the LowestSignificantDigits fix for #108). I checked that the two branches merge cleanly.

Tests

The new SignificantNumberModTests covers:

  • the repro lines, plus 6.8 % 2.27
  • rounding to the fewest decimal places
  • |result| < |divisor| across a spread of operands, including a negative divisor

With the fix reverted, 10 cases fail. With it, the whole suite passes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QBrEwaLGeo2SvjVK3AnLrp


Generated by Claude Code

Mod rounded the remainder to the operands' fewest significant digits, which
could carry it up to the divisor itself (19.9 % 2 == 2, 9.96 % 5 == 5). A
remainder is a subtraction, so round it to the fewest decimal places as
Subtract does, and return zero when that rounding still reaches the divisor or
beyond, since the remainder is then indistinguishable from a whole multiple.

Update the README and class remarks, which said modulus followed the
significant-digits rule.

Fixes #109

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBrEwaLGeo2SvjVK3AnLrp
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 7716681 into main Sep 28, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the fix/mod-remainder-equals-divisor branch September 28, 2026 01:43
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.

% can return a remainder equal to the divisor: 19.9 % 2 == 2 and 9.96 % 5 == 5

2 participants