Skip to content

Commit 5b3bbd6

Browse files
committed
Reject a precision below one in ReduceSignificance [patch]
ReduceSignificance did not validate its argument, so 5 reduced to zero significant digits returned 10, and to minus one returned 0, with no error. It now throws ArgumentOutOfRangeException below one, as Divide, the roots, Sin and the constant accessors already do. The exponent addition is now checked too, following the exponent-overflow fix in #120, so a result beyond int's exponent range throws OverflowException instead of wrapping. Fixes #126 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LasidbyRZfyxgW8Z9wQu9M
1 parent 5a9f69d commit 5b3bbd6

2 files changed

Lines changed: 28 additions & 3 deletions

File tree

‎PreciseNumber.Test/PreciseNumberTests.cs‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,26 @@ public void TestReduceSignificance()
10561056
Assert.AreEqual(number, number.ReduceSignificance(5));
10571057
}
10581058

1059+
// A precision below one has no meaning, and used to return a wrong number rather than fail:
1060+
// 5 reduced to zero digits came back as 10, and to minus one digit as 0.
1061+
1062+
[TestMethod]
1063+
[DataRow(0)]
1064+
[DataRow(-1)]
1065+
[DataRow(int.MinValue)]
1066+
public void TestReduceSignificanceRejectsAPrecisionBelowOne(int significantDigits)
1067+
{
1068+
Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => 5.ToPreciseNumber().ReduceSignificance(significantDigits));
1069+
Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => PreciseNumber.Pi.ReduceSignificance(significantDigits));
1070+
}
1071+
1072+
[TestMethod]
1073+
public void TestReduceSignificanceThrowsWhenTheExponentLeavesTheIntRange()
1074+
{
1075+
PreciseNumber number = PreciseNumber.CreateFromComponents(int.MaxValue - 1, 12345);
1076+
Assert.ThrowsExactly<OverflowException>(() => number.ReduceSignificance(3));
1077+
}
1078+
10591079
[TestMethod]
10601080
public void TestReduceSignificanceRoundsHalfAwayFromZeroOnTheDroppedDigits()
10611081
{

‎PreciseNumber/PreciseNumber.cs‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -857,8 +857,15 @@ internal long CountDecimalDigits() =>
857857
/// <param name="significantDigits">The number of significant digits to reduce to.</param>
858858
/// <returns>A new instance of <see cref="PreciseNumber"/> reduced to the specified number of significant digits.</returns>
859859
/// <remarks>Rounds half away from zero, so 123.5 becomes 124 and 123.456 becomes 123 at three digits.</remarks>
860+
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="significantDigits"/> is less than one.</exception>
861+
/// <exception cref="OverflowException">Thrown when the result needs an exponent outside the range of an <see cref="int"/>.</exception>
860862
public PreciseNumber ReduceSignificance(int significantDigits)
861863
{
864+
if (significantDigits < 1)
865+
{
866+
throw new ArgumentOutOfRangeException(nameof(significantDigits), significantDigits, "At least one significant digit is required.");
867+
}
868+
862869
int significantDifference = significantDigits < SignificantDigits
863870
? SignificantDigits - significantDigits
864871
: 0;
@@ -868,9 +875,7 @@ public PreciseNumber ReduceSignificance(int significantDigits)
868875
return this;
869876
}
870877

871-
int newExponent = Exponent == 0
872-
? significantDifference
873-
: Exponent + significantDifference;
878+
int newExponent = checked(Exponent + significantDifference);
874879
BigInteger newSignificand = DropDigitsRoundingHalfAwayFromZero(Significand, significantDifference);
875880
return new(newExponent, newSignificand);
876881
}

0 commit comments

Comments
 (0)