PageRenderTime 13ms CodeModel.GetById 1ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 1ms

/src/NUnit/framework/Constraints/FloatingPointNumerics.cs

#
C# | 238 lines | 89 code | 32 blank | 117 comment | 0 complexity | 24b39dcae0773683ab3ad510e1da42a6 MD5 | raw file
  1// ****************************************************************
  2// Copyright 2008, Charlie Poole
  3// This is free software licensed under the NUnit license. You may
  4// obtain a copy of the license at http://nunit.org
  5// ****************************************************************
  6
  7#if !NETCF_1_0
  8
  9using System;
 10using System.Runtime.InteropServices;
 11
 12namespace NUnit.Framework.Constraints
 13{
 14
 15    /// <summary>Helper routines for working with floating point numbers</summary>
 16    /// <remarks>
 17    ///   <para>
 18    ///     The floating point comparison code is based on this excellent article:
 19    ///     http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
 20    ///   </para>
 21    ///   <para>
 22    ///     "ULP" means Unit in the Last Place and in the context of this library refers to
 23    ///     the distance between two adjacent floating point numbers. IEEE floating point
 24    ///     numbers can only represent a finite subset of natural numbers, with greater
 25    ///     accuracy for smaller numbers and lower accuracy for very large numbers.
 26    ///   </para>
 27    ///   <para>
 28    ///     If a comparison is allowed "2 ulps" of deviation, that means the values are
 29    ///     allowed to deviate by up to 2 adjacent floating point values, which might be
 30    ///     as low as 0.0000001 for small numbers or as high as 10.0 for large numbers.
 31    ///   </para>
 32    /// </remarks>
 33    public class FloatingPointNumerics
 34    {
 35
 36        #region struct FloatIntUnion
 37
 38        /// <summary>Union of a floating point variable and an integer</summary>
 39        [StructLayout(LayoutKind.Explicit)]
 40        private struct FloatIntUnion
 41        {
 42            /// <summary>The union's value as a floating point variable</summary>
 43            [FieldOffset(0)]
 44            public float Float;
 45
 46            /// <summary>The union's value as an integer</summary>
 47            [FieldOffset(0)]
 48            public int Int;
 49
 50            /// <summary>The union's value as an unsigned integer</summary>
 51            [FieldOffset(0)]
 52            public uint UInt;
 53        }
 54
 55        #endregion // struct FloatIntUnion
 56
 57        #region struct DoubleLongUnion
 58
 59        /// <summary>Union of a double precision floating point variable and a long</summary>
 60        [StructLayout(LayoutKind.Explicit)]
 61        private struct DoubleLongUnion
 62        {
 63            /// <summary>The union's value as a double precision floating point variable</summary>
 64            [FieldOffset(0)]
 65            public double Double;
 66
 67            /// <summary>The union's value as a long</summary>
 68            [FieldOffset(0)]
 69            public long Long;
 70
 71            /// <summary>The union's value as an unsigned long</summary>
 72            [FieldOffset(0)]
 73            public ulong ULong;
 74        }
 75
 76        #endregion // struct DoubleLongUnion
 77
 78        /// <summary>Compares two floating point values for equality</summary>
 79        /// <param name="left">First floating point value to be compared</param>
 80        /// <param name="right">Second floating point value t be compared</param>
 81        /// <param name="maxUlps">
 82        ///   Maximum number of representable floating point values that are allowed to
 83        ///   be between the left and the right floating point values
 84        /// </param>
 85        /// <returns>True if both numbers are equal or close to being equal</returns>
 86        /// <remarks>
 87        ///   <para>
 88        ///     Floating point values can only represent a finite subset of natural numbers.
 89        ///     For example, the values 2.00000000 and 2.00000024 can be stored in a float,
 90        ///     but nothing inbetween them.
 91        ///   </para>
 92        ///   <para>
 93        ///     This comparison will count how many possible floating point values are between
 94        ///     the left and the right number. If the number of possible values between both
 95        ///     numbers is less than or equal to maxUlps, then the numbers are considered as
 96        ///     being equal.
 97        ///   </para>
 98        ///   <para>
 99        ///     Implementation partially follows the code outlined here:
100        ///     http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/
101        ///   </para>
102        /// </remarks>
103        public static bool AreAlmostEqualUlps(float left, float right, int maxUlps)
104        {
105            FloatIntUnion leftUnion = new FloatIntUnion();
106            FloatIntUnion rightUnion = new FloatIntUnion();
107
108            leftUnion.Float = left;
109            rightUnion.Float = right;
110
111            uint leftSignMask = (leftUnion.UInt >> 31);
112            uint rightSignMask = (rightUnion.UInt >> 31);
113
114            uint leftTemp = ((0x80000000 - leftUnion.UInt) & leftSignMask);
115            leftUnion.UInt = leftTemp | (leftUnion.UInt & ~leftSignMask);
116
117            uint rightTemp = ((0x80000000 - rightUnion.UInt) & rightSignMask);
118            rightUnion.UInt = rightTemp | (rightUnion.UInt & ~rightSignMask);
119
120            return (Math.Abs(leftUnion.Int - rightUnion.Int) <= maxUlps);
121        }
122
123        /// <summary>Compares two double precision floating point values for equality</summary>
124        /// <param name="left">First double precision floating point value to be compared</param>
125        /// <param name="right">Second double precision floating point value t be compared</param>
126        /// <param name="maxUlps">
127        ///   Maximum number of representable double precision floating point values that are
128        ///   allowed to be between the left and the right double precision floating point values
129        /// </param>
130        /// <returns>True if both numbers are equal or close to being equal</returns>
131        /// <remarks>
132        ///   <para>
133        ///     Double precision floating point values can only represent a limited series of
134        ///     natural numbers. For example, the values 2.0000000000000000 and 2.0000000000000004
135        ///     can be stored in a double, but nothing inbetween them.
136        ///   </para>
137        ///   <para>
138        ///     This comparison will count how many possible double precision floating point
139        ///     values are between the left and the right number. If the number of possible
140        ///     values between both numbers is less than or equal to maxUlps, then the numbers
141        ///     are considered as being equal.
142        ///   </para>
143        ///   <para>
144        ///     Implementation partially follows the code outlined here:
145        ///     http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/
146        ///   </para>
147        /// </remarks>
148        public static bool AreAlmostEqualUlps(double left, double right, long maxUlps)
149        {
150            DoubleLongUnion leftUnion = new DoubleLongUnion();
151            DoubleLongUnion rightUnion = new DoubleLongUnion();
152
153            leftUnion.Double = left;
154            rightUnion.Double = right;
155
156            ulong leftSignMask = (leftUnion.ULong >> 63);
157            ulong rightSignMask = (rightUnion.ULong >> 63);
158
159            ulong leftTemp = ((0x8000000000000000 - leftUnion.ULong) & leftSignMask);
160            leftUnion.ULong = leftTemp | (leftUnion.ULong & ~leftSignMask);
161
162            ulong rightTemp = ((0x8000000000000000 - rightUnion.ULong) & rightSignMask);
163            rightUnion.ULong = rightTemp | (rightUnion.ULong & ~rightSignMask);
164
165            return (Math.Abs(leftUnion.Long - rightUnion.Long) <= maxUlps);
166        }
167
168        /// <summary>
169        ///   Reinterprets the memory contents of a floating point value as an integer value
170        /// </summary>
171        /// <param name="value">
172        ///   Floating point value whose memory contents to reinterpret
173        /// </param>
174        /// <returns>
175        ///   The memory contents of the floating point value interpreted as an integer
176        /// </returns>
177        public static int ReinterpretAsInt(float value)
178        {
179            FloatIntUnion union = new FloatIntUnion();
180            union.Float = value;
181            return union.Int;
182        }
183
184        /// <summary>
185        ///   Reinterprets the memory contents of a double precision floating point
186        ///   value as an integer value
187        /// </summary>
188        /// <param name="value">
189        ///   Double precision floating point value whose memory contents to reinterpret
190        /// </param>
191        /// <returns>
192        ///   The memory contents of the double precision floating point value
193        ///   interpreted as an integer
194        /// </returns>
195        public static long ReinterpretAsLong(double value)
196        {
197            DoubleLongUnion union = new DoubleLongUnion();
198            union.Double = value;
199            return union.Long;
200        }
201
202        /// <summary>
203        ///   Reinterprets the memory contents of an integer as a floating point value
204        /// </summary>
205        /// <param name="value">Integer value whose memory contents to reinterpret</param>
206        /// <returns>
207        ///   The memory contents of the integer value interpreted as a floating point value
208        /// </returns>
209        public static float ReinterpretAsFloat(int value)
210        {
211            FloatIntUnion union = new FloatIntUnion();
212            union.Int = value;
213            return union.Float;
214        }
215
216        /// <summary>
217        ///   Reinterprets the memory contents of an integer value as a double precision
218        ///   floating point value
219        /// </summary>
220        /// <param name="value">Integer whose memory contents to reinterpret</param>
221        /// <returns>
222        ///   The memory contents of the integer interpreted as a double precision
223        ///   floating point value
224        /// </returns>
225        public static double ReinterpretAsDouble(long value)
226        {
227            DoubleLongUnion union = new DoubleLongUnion();
228            union.Long = value;
229            return union.Double;
230        }
231
232		private FloatingPointNumerics()
233		{
234		}
235    }
236}
237
238#endif