Skip to content

Take an odd root of an integer MinValue without recursing forever [patch] - #472

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/464-rootn-minvalue
Oct 20, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/464-rootn-minvalue

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #464

What was wrong

StorageMath.RootN handled a negative value with an odd degree by returning -RootN(-value, n). For a two's-complement integer's MinValue, -value wraps back to MinValue. RootN therefore called itself with the same arguments until the stack overflowed, which ends the process. This hit Cbrt(sbyte.MinValue), RootN(int.MinValue, 5), and the MinValue of short, long and Int128.

Change

  • When negating gives a value that is still negative, RootN roots -(value + 1), the magnitude one below. The floors of the two roots differ only when |MinValue| is itself an exact nth power, and then by exactly one.
  • A new IsExactPower helper checks that case by dividing value by the candidate root n times, to see whether it leaves exactly -1. Division keeps everything in range, so nothing overflows.
  • The result keeps the documented contract: the floor towards zero, and the exact root where there is one. For example, Cbrt(long.MinValue) is -2097152 (2^21) and RootN(sbyte.MinValue, 7) is -2.
  • Ordinary negative inputs take the same path as before. The even-root branch is unchanged, only reshaped into an if.

Tests

New test: OddRootOfIntegerMinValueIsTheExactFloor. It covers sbyte, short, int, long and Int128 with Cbrt and odd RootN, including the exact-power cases. The expected values were cross-checked with arbitrary-precision integer arithmetic.

  • With the StorageMath.cs change reverted, the test host dies with Stack overflow.
  • With the change, the full suite passes: 1780 succeeded, 0 failed locally on net10.0. The net9.0 leg could not launch in my environment, which has no .NET 9 runtime.

Generated by Claude Code

…tch]

-MinValue wraps back to MinValue, so RootN called itself with the same
arguments until the stack overflowed, which ends the process. It now
roots the magnitude one below and adds one where MinValue's magnitude
is an exact nth power, checked by division so nothing overflows.

Fixes #464

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

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 8fb8b7b into main Oct 20, 2026
16 checks passed
@matt-edmondson
matt-edmondson deleted the fix/464-rootn-minvalue branch October 20, 2026 01:11
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.

StorageMath.Cbrt / RootN crash the process with a stack overflow for an integer type's MinValue: Cbrt((sbyte)-128), RootN(int.MinValue, 5)

2 participants