diff --git a/Nuclex.Support (net-4.0).csproj b/Nuclex.Support (net-4.0).csproj index e01352e..65e37d4 100644 --- a/Nuclex.Support (net-4.0).csproj +++ b/Nuclex.Support (net-4.0).csproj @@ -188,7 +188,7 @@ - TransformingReadOnlyCollection.cs + Variegator.cs diff --git a/Nuclex.Support (xna-4.0-phone7).csproj b/Nuclex.Support (xna-4.0-phone7).csproj index 65fc357..1cc52ce 100644 --- a/Nuclex.Support (xna-4.0-phone7).csproj +++ b/Nuclex.Support (xna-4.0-phone7).csproj @@ -217,6 +217,10 @@ TransformingReadOnlyCollection.cs + + + Variegator.cs + WeakCollection.cs diff --git a/Nuclex.Support (xna-4.0-xbox360).csproj b/Nuclex.Support (xna-4.0-xbox360).csproj index e2fcedb..7c6607f 100644 --- a/Nuclex.Support (xna-4.0-xbox360).csproj +++ b/Nuclex.Support (xna-4.0-xbox360).csproj @@ -228,6 +228,10 @@ TransformingReadOnlyCollection.cs + + + Variegator.cs + WeakCollection.cs diff --git a/Source/Collections/Variegator.Test.cs b/Source/Collections/Variegator.Test.cs index 7f0086e..a28b3e7 100644 --- a/Source/Collections/Variegator.Test.cs +++ b/Source/Collections/Variegator.Test.cs @@ -29,7 +29,7 @@ namespace Nuclex.Support.Collections { /// Unit Test for the Variegator multi dictionary [TestFixture] - public class VariegatorTest { + internal class VariegatorTest { /// /// Tests whether the default constructor of the reverse comparer works diff --git a/Source/Collections/Variegator.cs b/Source/Collections/Variegator.cs index 4e03563..d2397c7 100644 --- a/Source/Collections/Variegator.cs +++ b/Source/Collections/Variegator.cs @@ -20,7 +20,6 @@ License along with this library using System; using System.Collections.Generic; -using System.Text; namespace Nuclex.Support.Collections { diff --git a/Source/FloatHelper.Test.cs b/Source/FloatHelper.Test.cs index 82d0bb4..accf4d4 100644 --- a/Source/FloatHelper.Test.cs +++ b/Source/FloatHelper.Test.cs @@ -29,11 +29,11 @@ namespace Nuclex.Support { /// Unit Test for the FloatHelper class [TestFixture] - internal class FloatHelperTest { + public class FloatHelperTest { /// Tests the floating point value comparison helper [Test] - public void TestFloatComparison() { + public void UlpDistancesOnFloatsCompareAsEqual() { Assert.IsTrue( FloatHelper.AreAlmostEqual(0.00000001f, 0.0000000100000008f, 1), "Minimal difference between very small floating point numbers is considered equal" @@ -55,7 +55,7 @@ namespace Nuclex.Support { /// Tests the double precision floating point value comparison helper [Test] - public void TestDoubleComparison() { + public void UlpDistancesOnDoublesCompareAsEqual() { Assert.IsTrue( FloatHelper.AreAlmostEqual(0.00000001, 0.000000010000000000000002, 1), "Minimal difference between very small double precision floating point " + @@ -81,7 +81,7 @@ namespace Nuclex.Support { /// Tests the integer reinterpretation functions [Test] - public void TestIntegerReinterpretation() { + public void IntegersCanBeReinterpretedAsFloats() { Assert.AreEqual( 12345.0f, FloatHelper.ReinterpretAsFloat(FloatHelper.ReinterpretAsInt(12345.0f)), @@ -91,7 +91,7 @@ namespace Nuclex.Support { /// Tests the long reinterpretation functions [Test] - public void TestLongReinterpretation() { + public void LongsCanBeReinterpretedAsDoubles() { Assert.AreEqual( 12345.67890, FloatHelper.ReinterpretAsDouble(FloatHelper.ReinterpretAsLong(12345.67890)), @@ -101,7 +101,7 @@ namespace Nuclex.Support { /// Tests the floating point reinterpretation functions [Test] - public void TestFloatReinterpretation() { + public void FloatsCanBeReinterpretedAsIntegers() { Assert.AreEqual( 12345, FloatHelper.ReinterpretAsInt(FloatHelper.ReinterpretAsFloat(12345)), @@ -109,12 +109,33 @@ namespace Nuclex.Support { ); } + /// + /// Verifies that the IsZero() method can distinguish zero from very small values + /// + [Test] + public void CanDetermineIfFloatIsZero() { + Assert.IsTrue(FloatHelper.IsZero(FloatHelper.PositiveZeroFloat)); + Assert.IsTrue(FloatHelper.IsZero(FloatHelper.NegativeZeroFloat)); + Assert.IsFalse(FloatHelper.IsZero(1.401298E-45f)); + Assert.IsFalse(FloatHelper.IsZero(-1.401298E-45f)); + } + + /// + /// Verifies that the IsZero() method can distinguish zero from very small values + /// + [Test] + public void CanDetermineIfDoubleIsZero() { + Assert.IsTrue(FloatHelper.IsZero(FloatHelper.PositiveZeroDouble)); + Assert.IsTrue(FloatHelper.IsZero(FloatHelper.NegativeZeroDouble)); + Assert.IsFalse(FloatHelper.IsZero(4.94065645841247E-324)); + Assert.IsFalse(FloatHelper.IsZero(-4.94065645841247E-324)); + } /// /// Tests the double prevision floating point reinterpretation functions /// [Test] - public void TestDoubleReinterpretation() { + public void DoublesCanBeReinterpretedAsLongs() { Assert.AreEqual( 1234567890, FloatHelper.ReinterpretAsLong(FloatHelper.ReinterpretAsDouble(1234567890)), @@ -122,6 +143,26 @@ namespace Nuclex.Support { ); } + // http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ + // Make both positive + // If both are negative -> fine + // If both are positive -> fine + // If different -> Measure both distances to zero in ulps and sum them + public void NegativeZeroEqualsPositiveZero() { + float zero = 0.0f; + float zeroPlusOneUlp = FloatHelper.ReinterpretAsFloat( + FloatHelper.ReinterpretAsInt(zero) + 1 + ); + float zeroMinusOneUlp = -zeroPlusOneUlp; + + bool test = FloatHelper.AreAlmostEqual(zeroMinusOneUlp, zeroPlusOneUlp, 1); + + Assert.IsFalse(FloatHelper.AreAlmostEqual(zero, zeroPlusOneUlp, 0)); + Assert.IsTrue(FloatHelper.AreAlmostEqual(zero, zeroPlusOneUlp, 1)); + Assert.IsFalse(FloatHelper.AreAlmostEqual(zero, zeroMinusOneUlp, 0)); + Assert.IsTrue(FloatHelper.AreAlmostEqual(zero, zeroMinusOneUlp, 1)); + } + } } // namespace Nuclex.Support diff --git a/Source/FloatHelper.cs b/Source/FloatHelper.cs index ce4636a..0833753 100644 --- a/Source/FloatHelper.cs +++ b/Source/FloatHelper.cs @@ -88,6 +88,40 @@ namespace Nuclex.Support { #endregion // struct DoubleLongUnion + /// A floating point value that holds a positive zero + public const float PositiveZeroFloat = +0.0f; + + /// A floating point value that holds a negative zero + /// + /// Negative zeros have a special representation in IEEE 752 floating point math + /// + public const float NegativeZeroFloat = -0.0f; + + /// A double precision floating point value that holds a positive zero + public const double PositiveZeroDouble = +0.0; + + /// A doublep precision floating point value that holds a negative zero + /// + /// Negative zeros have a special representation in IEEE 752 floating point math + /// + public const double NegativeZeroDouble = -0.0; + + /// Checks whether the floating point value is exactly zero + /// Value that will be checked for being zero + /// True if the value is zero, false otherwise + public static bool IsZero(float value) { + return (value == PositiveZeroFloat) || (value == NegativeZeroFloat); + } + + /// + /// Checks whether the double precision floating point value is exactly zero + /// + /// Value that will be checked for being zero + /// True if the value is zero, false otherwise + public static bool IsZero(double value) { + return (value == PositiveZeroDouble) || (value == NegativeZeroDouble); + } + /// Compares two floating point values for equality /// First floating point value to be compared /// Second floating point value t be compared