diff --git a/src/Numerics/Complex32.cs b/src/Numerics/Complex32.cs
index a134cc38..519bab95 100644
--- a/src/Numerics/Complex32.cs
+++ b/src/Numerics/Complex32.cs
@@ -180,11 +180,35 @@ namespace MathNet.Numerics
///
/// Gets the magnitude (or absolute value) of a complex number.
///
+ /// Assuming that magnitude of (inf,a) and (a,inf) and (inf,inf) is inf and (NaN,a), (a,NaN) and (NaN,NaN) is NaN
/// The magnitude of the current instance.
public float Magnitude
{
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
- get { return (float)Math.Sqrt((_real * _real) + (_imag * _imag)); }
+ get
+ {
+ if (float.IsNaN(_real) || float.IsNaN(_imag))
+ return float.NaN;
+ if (float.IsInfinity(_real) || float.IsInfinity(_imag))
+ return float.PositiveInfinity;
+ float a = Math.Abs(_real);
+ float b = Math.Abs(_imag);
+ if (a > b)
+ {
+ double tmp = b / a;
+ return a * (float)Math.Sqrt(1.0f + tmp * tmp);
+
+ }
+ if (a == 0.0f) // one can write a >= float.Epsilon here
+ {
+ return b;
+ }
+ else
+ {
+ double tmp = a / b;
+ return b * (float)Math.Sqrt(1.0f + tmp * tmp);
+ }
+ }
}
///
@@ -485,9 +509,9 @@ namespace MathNet.Numerics
///
public Tuple CubicRoots()
{
- float r = (float)Math.Pow(Magnitude, 1d/3d);
- float theta = Phase/3;
- const float shift = (float)Constants.Pi2/3;
+ float r = (float)Math.Pow(Magnitude, 1d / 3d);
+ float theta = Phase / 3;
+ const float shift = (float)Constants.Pi2 / 3;
return new Tuple(
FromPolarCoordinates(r, theta),
FromPolarCoordinates(r, theta + shift),
@@ -620,6 +644,8 @@ namespace MathNet.Numerics
}
/// Division operator. Divides a complex number by another.
+ /// Enhanced Smith's algorithm for dividing two complex numbers
+ ///
/// The result of the division.
/// The dividend.
/// The divisor.
@@ -634,14 +660,46 @@ namespace MathNet.Numerics
{
return PositiveInfinity;
}
-
- var modSquared = divisor.MagnitudeSquared;
- return new Complex32(
- ((dividend._real * divisor._real) + (dividend._imag * divisor._imag)) / modSquared,
- ((dividend._imag * divisor._real) - (dividend._real * divisor._imag)) / modSquared);
+ float a = dividend.Real;
+ float b = dividend.Imaginary;
+ float c = divisor.Real;
+ float d = divisor.Imaginary;
+ if (Math.Abs(d) <= Math.Abs(c))
+ return InternalDiv(a, b, c, d, false);
+ return InternalDiv(b, a, d, c, true);
+ }
+ ///
+ /// Helper method for dividing.
+ ///
+ /// Re first
+ /// Im first
+ /// Re second
+ /// Im second
+ ///
+ ///
+ private static Complex32 InternalDiv(float a, float b, float c, float d, bool swapped)
+ {
+ float r = d / c;
+ float t = 1 / (c + d * r);
+ float e, f;
+ if (r != 0.0f) // one can use r >= float.Epsilon || r <= float.Epsilon instead
+ {
+ e = (a + b * r) * t;
+ f = (b - a * r) * t;
+ }
+ else
+ {
+ e = (a + d * (b / c)) * t;
+ f = (b - d * (a / c)) * t;
+ }
+ if (swapped)
+ f = -f;
+ return new Complex32(e, f);
}
/// Division operator. Divides a float value by a complex number.
+ /// Algorithm based on Smith's algorithm
+ ///
/// The result of the division.
/// The dividend.
/// The divisor.
@@ -656,9 +714,11 @@ namespace MathNet.Numerics
{
return PositiveInfinity;
}
-
- var zmod = divisor.MagnitudeSquared;
- return new Complex32(dividend * divisor._real / zmod, -dividend * divisor._imag / zmod);
+ float c = divisor.Real;
+ float d = divisor.Imaginary;
+ if (Math.Abs(d) <= Math.Abs(c))
+ return InternalDiv(dividend, 0, c, d, false);
+ return InternalDiv(0, dividend, d, c, true);
}
/// Division operator. Divides a complex number by a float value.
@@ -1272,7 +1332,6 @@ namespace MathNet.Numerics
{
return dividend / divisor;
}
-
///
/// Returns the multiplicative inverse of a complex number.
///
diff --git a/src/UnitTests/ComplexTests/Complex32Test.cs b/src/UnitTests/ComplexTests/Complex32Test.cs
index ec28709c..c7f20678 100644
--- a/src/UnitTests/ComplexTests/Complex32Test.cs
+++ b/src/UnitTests/ComplexTests/Complex32Test.cs
@@ -31,7 +31,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests
{
#if NOSYSNUMERICS
using Complex = Numerics.Complex;
-#else
+#else
using Complex = System.Numerics.Complex;
#endif
@@ -417,7 +417,54 @@ namespace MathNet.Numerics.UnitTests.ComplexTests
Assert.AreEqual(new Complex32(-2, 0), new Complex32(4, -4) / new Complex32(-2, 2));
Assert.AreEqual(Complex32.PositiveInfinity, Complex32.One / Complex32.Zero);
}
+ ///
+ /// Can divide without overflow.
+ ///
+ [Test]
+ public void CanDodgeOverflowDivision()
+ {
+ var first = new Complex32((float)Math.Pow(10, 37), (float)Math.Pow(10, -37));
+ var second = new Complex32((float)Math.Pow(10, 25), (float)Math.Pow(10, -25));
+ Assert.AreEqual(new Complex32((float)Math.Pow(10, 12), (float)Math.Pow(10, -38)), first / second);
+
+ first = new Complex32(-(float)Math.Pow(10, 37), (float)Math.Pow(10, -37));
+ second = new Complex32((float)Math.Pow(10, 25), (float)Math.Pow(10, -25));
+ Assert.AreEqual(new Complex32(-(float)Math.Pow(10, 12), (float)Math.Pow(10, -38)), first / second);
+
+ first = new Complex32((float)Math.Pow(10, -37), (float)Math.Pow(10, 37));
+ second = new Complex32((float)Math.Pow(10, -17), -(float)Math.Pow(10, 17));
+ Assert.AreEqual(new Complex32(-(float)Math.Pow(10, 20), (float)Math.Pow(10, -14)), first / second);
+
+ }
+ ///
+ /// Can divide float/complex without overflow
+ ///
+ [Test]
+ public void CanDodgeOverflowDivisionFloat()
+ {
+
+ var firstComplex = new Complex32((float)Math.Pow(10, 25), (float)Math.Pow(10, -25));
+ float firstFloat = (float)Math.Pow(10, -37);
+ float secondFloat = (float)Math.Pow(10, 37);
+ Assert.AreEqual(new Complex32(0, 0), firstFloat / firstComplex); // it's (10^-62,10^-112) thus overflow to 0
+ Assert.AreEqual(new Complex32((float)Math.Pow(10, 12), (float)Math.Pow(10, -38)), secondFloat / firstComplex);
+
+ var secondComplex = new Complex32((float)Math.Pow(10, -25), (float)Math.Pow(10, 25));
+ Assert.AreEqual(new Complex32(0, 0), firstFloat / secondComplex);// it's (10^-112,10^-62) thus overflow to 0
+ Assert.AreEqual(new Complex32((float)Math.Pow(10, -38), -(float)Math.Pow(10, 12)), secondFloat / secondComplex);
+
+
+
+ float thirdFloat = (float)Math.Pow(10, 13);
+ var thirdComplex = new Complex32((float)Math.Pow(10, -25), (float)Math.Pow(10, -25));
+ Assert.AreEqual(new Complex32(5.0f * (float)Math.Pow(10, 37), -5.0f * (float)Math.Pow(10, 37)), thirdFloat / thirdComplex);
+
+ var fourthFloat = (float)Math.Pow(10, -30);
+ var fourthComplex = new Complex32((float)Math.Pow(10, -30), (float)Math.Pow(10, -30));
+ Assert.AreEqual(new Complex32(0.5f, -0.5f), fourthFloat / fourthComplex);
+
+ }
///
/// Can multiple a complex number and a double using operators.
///
@@ -547,7 +594,17 @@ namespace MathNet.Numerics.UnitTests.ComplexTests
{
Assert.AreEqual(expected, new Complex32(real, imag).Magnitude);
}
+ ///
+ /// Can calculate magnitude without overflow
+ ///
+ [Test]
+ public void CanDodgeOverflowMagnitude()
+ {
+ Assert.AreEqual((float)Math.Sqrt(2) * float.Epsilon, new Complex32(float.Epsilon, float.Epsilon).Magnitude);
+ Assert.AreEqual(float.Epsilon, new Complex32(0, float.Epsilon).Magnitude);
+ Assert.AreEqual((float)(Math.Pow(10, 30) * Math.Sqrt(2)), new Complex32((float)Math.Pow(10, 30), (float)Math.Pow(10, 30)).Magnitude);
+ }
///
/// Can compute sign.
///