Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,21 @@ anything else is `Complex.Pow` as before. At finite positive points the two agre
| `"x^(-1)".ToEntity().Compile("x").Call(0)` | `(0, 0)` | `(NaN, 0)` |
| `"x^0".ToEntity().Compile("x").Call(0)` | `(1, 0)` | `(NaN, 0)` |

### A compiled power over `double` of a value that does not exist has none

**Silent: a power over `double` whose base or exponent is `NaN` is `NaN`, where a zero exponent or a base of `1` made
it `1`.** `Compile<double, ...>` and `Compile<float, ...>` took a power by `Math.Pow`, for which `NaN^0` and `1^NaN` are
`1`, so a power of a value that does not exist had one. FastExpression and the compilers over `Complex` have it as
`NaN`, as evaluation does ([#2250](https://github.com/asc-community/AngouriMath/issues/2250)). Over `double` a `NaN`
also stands for a value that is not real, so `(sqrt(x - 2))^0` at `0.7`, which is `1`, is `NaN` there now, as
`sqrt(x - 2) + 1 - sqrt(x - 2)` already was.

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"(x provided x > 1)^0".ToEntity().Compile<double, double>("x")(0.7)` | `1` | `NaN` |
| `"1^(x provided x > 1)".ToEntity().Compile<double, double>("x")(0.7)` | `1` | `NaN` |
| `"x^0".ToEntity().Compile<double, double>("x")(double.NaN)` | `1` | `NaN` |

### A compiled statement with no truth value has none

**Silent in part: a compiled comparison has the truth value evaluation gives it.** A comparison over `Complex` was refused
Expand Down
3 changes: 3 additions & 0 deletions Sources/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -938,5 +938,8 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
[Tests/UnitTests/Common/RealPowerCompilationTest.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/Common/PowerOfNaNOverDoubleTest.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/Common/BranchCutCompilationTest.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
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ public static BigInteger Log(BigInteger a, BigInteger b)

public static System.Numerics.Complex Pow(System.Numerics.Complex a, System.Numerics.Complex b)
=> AngouriMath.Core.Compilation.RealProducts.Power(a, b);
// A power of a number that does not exist, or to such a power, has no value, as in evaluation and the other
// compilers: Math.Pow takes NaN^0 and 1^NaN as 1. https://github.com/asc-community/AngouriMath/issues/2250
public static double Pow(double a, double b)
=> Math.Pow(a, b);
=> double.IsNaN(a) || double.IsNaN(b) ? double.NaN : Math.Pow(a, b);
public static float Pow(float a, float b)
=> (float)Math.Pow(a, b);
=> float.IsNaN(a) || float.IsNaN(b) ? float.NaN : (float)Math.Pow(a, b);
public static long Pow(long a, long b)
=> (long)Math.Pow(a, b);
public static int Pow(int a, int b)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@ namespace AngouriMath.Core.Compilation.IntoLinq

public static System.Numerics.Complex Pow(System.Numerics.Complex a, System.Numerics.Complex b)
=> AngouriMath.Core.Compilation.RealProducts.Power(a, b);
// A power of a number that does not exist, or to such a power, has no value, as in evaluation and the other
// compilers: Math.Pow takes NaN^0 and 1^NaN as 1. https://github.com/asc-community/AngouriMath/issues/2250
public static double Pow(double a, double b)
=> Math.Pow(a, b);
=> double.IsNaN(a) || double.IsNaN(b) ? double.NaN : Math.Pow(a, b);
public static float Pow(float a, float b)
=> (float)Math.Pow(a, b);
=> float.IsNaN(a) || float.IsNaN(b) ? float.NaN : (float)Math.Pow(a, b);
public static long Pow(long a, long b)
=> (long)Math.Pow(a, b);
public static int Pow(int a, int b)
Expand Down
52 changes: 52 additions & 0 deletions Sources/Tests/UnitTests/Common/PowerOfNaNOverDoubleTest.cs
Original file line number Diff line number Diff line change
@@ -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 AngouriMath;
using AngouriMath.Extensions;
using Xunit;

namespace AngouriMath.Tests.Common
{
/// <summary>
/// Compiled over <see cref="double"/>, a power of a value that does not exist, or to such a power, has none, as in
/// evaluation and FastExpression, where <see cref="System.Math.Pow(double, double)"/> made <c>NaN^0</c> and <c>1^NaN</c> 1.
/// <a href="https://github.com/asc-community/AngouriMath/issues/2250">#2250</a>
/// </summary>
[Trait("Area", "Common")]
public sealed class PowerOfNaNOverDoubleTest
{
[Theory]
[InlineData("(x provided x > 1)^0", 0.7)]
[InlineData("1^(x provided x > 1)", 0.7)]
[InlineData("((x provided x > 1)^Im(x)) * sin(-2)", 0.7)]
[InlineData("x^0", double.NaN)]
[InlineData("1^x", double.NaN)]
public void ItHasNoValue(string expression, double at)
{
var overDoubles = expression.ToEntity().Compile<double, double>("x")(at);
Assert.True(double.IsNaN(overDoubles), $"{expression} at {at}: {overDoubles} over double");
Assert.True(double.IsNaN(expression.ToEntity().Compile("x").Call(at).Real), $"{expression} at {at} by FastExpression");
if (!double.IsNaN(at))
Assert.True(expression.ToEntity().Substitute("x", at).Evaled.IsNaN, $"{expression} at {at} evaluates to a value");
}

[Fact]
public void OverFloatToo()
{
Assert.True(float.IsNaN("x^0".ToEntity().Compile<float, float>("x")(float.NaN)));
Assert.True(float.IsNaN("1^x".ToEntity().Compile<float, float>("x")(float.NaN)));
}

[Theory]
[InlineData("x^0", 0.7, 1)]
[InlineData("1^x", 0.7, 1)]
[InlineData("x^2", -3, 9)]
[InlineData("x^(1/2)", 4, 2)]
public void WhereItHasOneItIsThatValue(string expression, double at, double value)
=> Assert.Equal(value, expression.ToEntity().Compile<double, double>("x")(at));
}
}
Loading