diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 47f838101..fba254fc7 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -92,6 +92,18 @@ solver's check that a limit agrees from both sides goes through the same compari
| `AreEqualNumerically("2 x (-1/4 + ln(x)/2) + 1/x * 2/4 * x^2", "x ln(x)")` | `false` | `true` |
| `AreEqualNumerically` of `x ln(x)` and its antiderivative's derivative | `false` | `true` |
+### A whole number the rational search finds is an integer
+
+**Silent: a wrong type and a wrong comparison.** The search that reads a number as the rational it is near built its
+result without the check that turns a denominator of 1 into an integer. A value just below a whole number came back as
+the `Rational` `1/1` or `3/1`, which is not an `Integer` and is not equal to `1` or `3`
+([#2069](https://github.com/asc-community/AngouriMath/issues/2069)).
+
+| Input | Was (2.6.0) | Now |
+|---|---|---|
+| `(Entity)0.9999999999999999999999999999m` | `Rational 1/1`, and `== 1` is `false` | `Integer 1` |
+| `(Entity)2.9999999999999999999999999999m` | `Rational 3/1` | `Integer 3` |
+
### A computed value is a rational only within half the working digits
**Silent: wrong answers.** A number an evaluation produced became the small rational it was near within a fixed
diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Rational.Definition.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Rational.Definition.cs
index aaaf8c0c4..8ada2fd05 100644
--- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Rational.Definition.cs
+++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Rational.Definition.cs
@@ -215,7 +215,10 @@ public static Rational Create(ERational value)
var rat = FindRationalExactly(inv, iterCount - 1, tolerance);
if (rat is null)
return null;
- return new Rational((intPart * sign + sign / rat.ERational).InLowestTerms());
+ // Through Create, so that a whole number is an Integer: a value just below one is 0 + 1/1, and
+ // came back as the Rational 1/1, which is not equal to 1.
+ // https://github.com/asc-community/AngouriMath/issues/2069
+ return Rational.Create(intPart * sign + sign / rat.ERational);
}
}
diff --git a/Sources/Tests/UnitTests/Core/NumericDowncasting.cs b/Sources/Tests/UnitTests/Core/NumericDowncasting.cs
index de1ddd3f0..aed399b0b 100644
--- a/Sources/Tests/UnitTests/Core/NumericDowncasting.cs
+++ b/Sources/Tests/UnitTests/Core/NumericDowncasting.cs
@@ -270,5 +270,20 @@ [Fact] public void FromRationalTestRealDowncastingEnabledInt()
);
#endregion
+
+ ///
+ /// A whole number the rational search finds is an : a value just below one came back
+ /// as the Rational 1/1, which is not equal to 1.
+ /// #2069
+ ///
+ [Theory]
+ [InlineData("0.9999999999999999999999999999", 1)]
+ [InlineData("2.9999999999999999999999999999", 3)]
+ public void AWholeNumberFromTheRationalSearchIsAnInteger(string written, int whole)
+ {
+ Entity value = decimal.Parse(written, System.Globalization.CultureInfo.InvariantCulture);
+ value.Should().BeOfType();
+ value.Should().Be((Entity)whole);
+ }
}
-}
\ No newline at end of file
+}