diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index fef54c9ea..59064adbc 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -139,6 +139,19 @@ and a number that passes every round is left open, since passing is not a proof | `"(10^30 + 1) in PP".ToEntity().Evaled` | `1000000000000000000000000000001 in PP` | `False` | | `"(10^30 + 57) in PP".ToEntity().Evaled` | `1000000000000000000000000000057 in PP` | `1000000000000000000000000000057 in PP` | +### `arcsin` and `arccos` of a real number past 1 have their value + +**Silent: `arcsin(x)` and `arccos(x)` of a real `x` past 1 are `sign(x) pi/2 - i arccosh|x|` and `pi/2 - arcsin(x)`, +where for about one argument in ten they were `NaN`.** The two share `(|w + 1| - |w - 1|) / 2`, which is in `[-1, 1]` by +the triangle inequality and exactly 1 for such an `x`. Rounded, it came out a little past 1 for some `x`, and the decimal +arcsine of that is `NaN`. Of `arcsin(k + c)` for `k` from 1 to 40 and `c` in `pi`, `e`, `sqrt(2)`, `sqrt(3)` and `ln(3)`, +20 were `NaN` ([#2244](https://github.com/asc-community/AngouriMath/issues/2244)). + +| Input | Was (2.6.0) | Now | +|---|---|---| +| `"arcsin(6 + pi)".ToEntity().Evaled` | `NaN` | `1.5707963267948966 - 2.902976737696685i`, to 15 digits | +| `"arccos(6 + pi)".ToEntity().Evaled` | `NaN` | `2.902976737696685i`, to 15 digits | + ### 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 006adb650..b677d246c 100644 --- a/Sources/.editorconfig +++ b/Sources/.editorconfig @@ -929,6 +929,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed [Tests/UnitTests/Convenience/SignumOfNaNTest.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/Convenience/ArcsineOfARealPastOneTest.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 + [AngouriMath/Functions/Compilation/RealProducts.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/Number/Operators.cs b/Sources/AngouriMath/Core/Entity/Continuous/Number/Operators.cs index 30c5620be..34eb8e533 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Number/Operators.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Number/Operators.cs @@ -947,7 +947,15 @@ public static Complex Cotan(Complex num) var rho = xp1.MultiplyAndAdd(xp1, y.Multiply(y, context), context).SqrtByIntegerRoot(context); var sigma = xm1.MultiplyAndAdd(xm1, y.Multiply(y, context), context).SqrtByIntegerRoot(context); var alpha = rho.Add(sigma, context).Divide(2, context); - return (rho.Subtract(sigma, context).Divide(2, context), + // beta is (|w + 1| - |w - 1|) / 2, in [-1, 1] by the triangle inequality and exactly 1 for a real w past + // 1; the two roots are rounded, and a beta of 1 + ε made arcsin(beta) NaN, as arcsin(6 + pi) was. + // https://github.com/asc-community/AngouriMath/issues/2244 + var beta = rho.Subtract(sigma, context).Divide(2, context); + if (beta.GreaterThan(EDecimal.One)) + beta = EDecimal.One; + else if (beta.LessThan(-EDecimal.One)) + beta = -EDecimal.One; + return (beta, alpha.MultiplyAndSubtract(alpha, EDecimal.One, context).SqrtByIntegerRoot(context).Add(alpha, context).NaturalLogarithm(context).Multiply((y.IsNegative || y.IsZero) ? -1 : 1, context)); } diff --git a/Sources/Tests/UnitTests/Convenience/ArcsineOfARealPastOneTest.cs b/Sources/Tests/UnitTests/Convenience/ArcsineOfARealPastOneTest.cs new file mode 100644 index 000000000..c7049e433 --- /dev/null +++ b/Sources/Tests/UnitTests/Convenience/ArcsineOfARealPastOneTest.cs @@ -0,0 +1,52 @@ +// +// 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 System.Numerics; +using AngouriMath; +using AngouriMath.Extensions; +using Xunit; + +namespace AngouriMath.Tests.Convenience +{ + /// + /// arcsin and arccos of a real number past 1 have their value, sign(x) pi/2 - i arccosh|x| and + /// pi/2 - arcsin(x), where for one argument in ten they were NaN: the rounded (|w + 1| - |w - 1|) / 2 + /// came out a little past 1, whose decimal arcsine is NaN. arcsin(6 + pi) was one. + /// #2244 + /// + [Trait("Area", "Convenience")] + public sealed class ArcsineOfARealPastOneTest + { + private static double Arccosh(double x) => System.Math.Log(x + System.Math.Sqrt(x * x - 1)); + + private static bool Agree(Complex expected, Complex actual) => Complex.Abs(expected - actual) <= 1e-12 * System.Math.Max(1, Complex.Abs(expected)); + + [Theory] + [InlineData("pi")] + [InlineData("e")] + [InlineData("sqrt(2)")] + [InlineData("sqrt(3)")] + [InlineData("ln(3)")] + public void BothHaveTheirValue(string constant) + { + for (var k = 1; k <= 40; k++) + foreach (var sign in new[] { "", "-" }) + { + var argument = $"{sign}({k} + {constant})"; + var x = ((Entity.Number.Complex)argument.ToEntity().Evaled).ToNumerics().Real; + if (System.Math.Abs(x) <= 1) + continue; + var arcsin = ((Entity.Number.Complex)$"arcsin({argument})".ToEntity().Evaled).ToNumerics(); + var arccos = ((Entity.Number.Complex)$"arccos({argument})".ToEntity().Evaled).ToNumerics(); + var expectedArcsin = new Complex(System.Math.Sign(x) * System.Math.PI / 2, -Arccosh(System.Math.Abs(x))); + var expectedArccos = new Complex(x > 0 ? 0 : System.Math.PI, Arccosh(System.Math.Abs(x))); + Assert.True(Agree(expectedArcsin, arcsin), $"arcsin({argument}): {arcsin}, where {expectedArcsin}"); + Assert.True(Agree(expectedArccos, arccos), $"arccos({argument}): {arccos}, where {expectedArccos}"); + } + } + } +}