From fc66077ea6fa85115f6f367cae34769a11f96a83 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 13:27:31 +0000 Subject: [PATCH] Round sums and differences to tens and hundreds when an operand is that coarse [patch] CountDecimalDigits clamped a positive exponent to 0, so an operand whose least significant digit is left of the decimal point was treated as exact to the units, and 1.2E3 + 34 gave 1234. Report that place as a negative decimal count instead, so Add, Subtract and Mod round to it: 1.2E3 + 34 is now 1.2E3, matching the two significant figures Multiply already honours. Fixes #107 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VeSBKr7Q99CTQ5BUJMKEct --- .../SignificantNumberAdditionPlaceTests.cs | 35 +++++++++++++++++++ SignificantNumber/SignificantNumber.cs | 12 +++---- 2 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 SignificantNumber.Test/SignificantNumberAdditionPlaceTests.cs diff --git a/SignificantNumber.Test/SignificantNumberAdditionPlaceTests.cs b/SignificantNumber.Test/SignificantNumberAdditionPlaceTests.cs new file mode 100644 index 0000000..49bb2df --- /dev/null +++ b/SignificantNumber.Test/SignificantNumberAdditionPlaceTests.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2023-2026 ktsu-dev contributors + +namespace SignificantNumber.Test; + +using System.Globalization; +using ktsu.SignificantNumber; + +/// +/// Covers the rounding of and to the +/// coarsest least significant place of their operands, including places left of the decimal point. +/// +[TestClass] +public class SignificantNumberAdditionPlaceTests +{ + private static SignificantNumber Parse(string text) => + SignificantNumber.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture); + + [TestMethod] + [DataRow("1.2E3", "34", "1.2E3")] + [DataRow("1200", "34", "1.2E3")] + [DataRow("34", "1.2E3", "1.2E3")] + [DataRow("1.2E3", "56", "1.3E3")] + [DataRow("1234", "5.6", "1240")] + [DataRow("1.2E3", "1", "1.2E3")] + public void Add_RoundsToTheCoarsestLeastSignificantPlace(string left, string right, string expected) => + Assert.AreEqual(Parse(expected), Parse(left) + Parse(right)); + + [TestMethod] + [DataRow("1.2E3", "34", "1.2E3")] + [DataRow("1200", "34", "1.2E3")] + [DataRow("1.2E3", "56", "1.1E3")] + [DataRow("1234", "5.6", "1228")] + public void Subtract_RoundsToTheCoarsestLeastSignificantPlace(string left, string right, string expected) => + Assert.AreEqual(Parse(expected), Parse(left) - Parse(right)); +} diff --git a/SignificantNumber/SignificantNumber.cs b/SignificantNumber/SignificantNumber.cs index c5cec45..9191bf9 100644 --- a/SignificantNumber/SignificantNumber.cs +++ b/SignificantNumber/SignificantNumber.cs @@ -145,14 +145,14 @@ private static bool HasInfinitePrecision(PreciseNumber value) => value.Exponent == 0 && BigInteger.Abs(value.Significand) <= BigInteger.One; /// - /// Counts the digits after the decimal point in a number. + /// Counts the decimal places of a number's least significant digit. /// /// The number to count the decimal digits of. - /// The number of digits after the decimal point. - private static int CountDecimalDigits(PreciseNumber value) => - value.Exponent > 0 - ? 0 - : int.Abs(value.Exponent); + /// + /// The number of digits after the decimal point, or, when the least significant digit is left of the decimal point, + /// the negated count of places it sits to the left of the units: -1 for tens, -2 for hundreds, and so on. + /// + private static int CountDecimalDigits(PreciseNumber value) => -value.Exponent; /// /// Gets the lower of the decimal digit counts of two numbers, ignoring an operand with unlimited precision.