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

#
C# | 238 lines | 89 code | 32 blank | 117 comment | 0 complexity | 24b39dcae0773683ab3ad510e1da42a6 MD5 | raw file
```  1// ****************************************************************
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
```