diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 4958f4e35..a065338c4 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -152,6 +152,20 @@ arcsine of that is `NaN`. Of `arcsin(k + c)` for `k` from 1 to 40 and `c` in `pi
| `"arcsin(6 + pi)".ToEntity().Evaled` | `NaN` | `1.5707963267948966 - 2.902976737696685i`, to 15 digits |
| `"arccos(6 + pi)".ToEntity().Evaled` | `NaN` | `2.902976737696685i`, to 15 digits |
+### An exact rational is zero, positive or negative as it is
+
+**Silent: 0 divided by a rational smaller than about 1e-199 is 0, where it was NaN.** Whether an exact rational was zero,
+and its sign, was read off its decimal, written out in the precision context, whose exponent range writes every rational
+under about 1e-199 in size as 0. So `0 / 10^(-200)` was a division by zero, and `0 ^ (1/10^250)` a power to an exponent
+that was not positive. A rational is now read by its exact value
+([#2277](https://github.com/asc-community/AngouriMath/issues/2277)).
+
+| Input | Was (2.6.0) | Now |
+|---|---|---|
+| `"0 / 10^(-200)".ToEntity().Evaled` | `NaN` | `0` |
+| `"0 / (3/10^260)".ToEntity().Evaled` | `NaN` | `0` |
+| `"0 ^ (1/10^250)".ToEntity().Evaled` | `NaN` | `0` |
+
### A system with a product equation is solved branch by branch
**Silent: solutions that were missing are answered.** `EquationSystem.Solve` eliminated `x * y = 0` for `y` as `0`,
diff --git a/Sources/.editorconfig b/Sources/.editorconfig
index 96598d54f..77b10a26d 100644
--- a/Sources/.editorconfig
+++ b/Sources/.editorconfig
@@ -66,6 +66,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
[Tests/UnitTests/Core/DividesTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n
+[Tests/UnitTests/Core/ATinyRationalIsNotZeroTest.cs]
+file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n
+
[Tests/UnitTests/Core/CardinalityTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n
diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Complex.Definition.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Complex.Definition.cs
index 829f33a64..b0d9b6c8f 100644
--- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Complex.Definition.cs
+++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Complex.Definition.cs
@@ -74,7 +74,15 @@ private protected Complex(Real? real, Real? imaginary) =>
///
/// Checks if both parts equal 0
///
- public new bool IsZero => RealPart.EDecimal.IsZero && ImaginaryPart.EDecimal.IsZero;
+ ///
+ /// A rational part is zero when it is, and not when its decimal is: written out in the
+ /// precision context, every rational under 1e-199 in size is 0, and 0 / 10^(-250) was NaN.
+ /// #2277
+ ///
+ public new bool IsZero => PartIsZero(RealPart) && PartIsZero(ImaginaryPart);
+
+ private static bool PartIsZero(Real part) =>
+ part is Rational rational ? rational.ERational.IsZero : part.EDecimal.IsZero;
///
/// Creates an instance of Complex
diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Real.Definition.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Real.Definition.cs
index c723d7a23..e89787d8f 100644
--- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Real.Definition.cs
+++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Real.Definition.cs
@@ -49,10 +49,17 @@ private protected Real() : base(null, null) { }
public override bool IsExact => !EDecimal.IsFinite;
/// Strictly less than 0
- public bool IsNegative => EDecimal.IsNegative;
+ ///
+ /// A rational's sign is its own, and not its decimal's: the decimal is written out in the
+ /// precision context, whose exponent range writes every rational under 1e-199 in size as
+ /// zero, so 0^(10^(-250)) was NaN.
+ /// #2277
+ ///
+ public bool IsNegative => this is Rational rational ? rational.ERational.Sign < 0 : EDecimal.IsNegative;
/// Strictly greater than 0
- public bool IsPositive => !EDecimal.IsNegative && !EDecimal.IsZero;
+ /// A rational's sign is its own, as for .
+ public bool IsPositive => this is Rational rational ? rational.ERational.Sign > 0 : !EDecimal.IsNegative && !EDecimal.IsZero;
///
/// Creates an instance of Real
diff --git a/Sources/Tests/UnitTests/Core/ATinyRationalIsNotZeroTest.cs b/Sources/Tests/UnitTests/Core/ATinyRationalIsNotZeroTest.cs
new file mode 100644
index 000000000..3b99bdb92
--- /dev/null
+++ b/Sources/Tests/UnitTests/Core/ATinyRationalIsNotZeroTest.cs
@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2019-2026 Angouri.
+// AngouriMath is licensed under MIT.
+// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
+// Website: https://am.angouri.org.
+//
+
+using AngouriMath.Extensions;
+using Xunit;
+using static AngouriMath.Entity.Number;
+
+namespace AngouriMath.Tests.Core
+{
+ ///
+ /// An exact rational is zero, positive or negative as it is, and not as its decimal is: the decimal is written out in the
+ /// precision context, whose exponent range writes every rational under 1e-199 in size as 0, so 0 / 10^(-200) was NaN.
+ /// #2277
+ ///
+ [Trait("Area", "Core")]
+ public sealed class ATinyRationalIsNotZeroTest
+ {
+ [Theory]
+ [InlineData("0 / 10^(-200)", "0")]
+ [InlineData("0 / 10^(-250)", "0")]
+ [InlineData("0 / (3/10^260)", "0")]
+ [InlineData("0 / (-1/10^300)", "0")]
+ [InlineData("0 ^ (1/10^250)", "0")]
+ [InlineData("0 / 10^(-199)", "0")]
+ [InlineData("10^(-250) * 10^250", "1")]
+ [InlineData("(1/10^250) / (1/10^250)", "1")]
+ public void ItIsWhatItIs(string expression, string value)
+ {
+ Assert.Equal(value.ToEntity(), expression.ToEntity().Evaled);
+ Assert.Equal(value.ToEntity(), expression.ToEntity().Simplify());
+ }
+
+ [Fact]
+ public void ItsSignIsItsOwn()
+ {
+ var tiny = Rational.Create(1, PeterO.Numbers.EInteger.FromInt32(10).Pow(250));
+ Assert.False(tiny.IsZero);
+ Assert.True(tiny.IsPositive);
+ Assert.False(tiny.IsNegative);
+ var negative = (Rational)(-tiny);
+ Assert.True(negative.IsNegative);
+ Assert.False(negative.IsPositive);
+ Assert.True(Integer.Zero.IsZero);
+ Assert.False(Integer.Zero.IsPositive || Integer.Zero.IsNegative);
+ }
+ }
+}