1 // ***********************************************************************
2 // Copyright (c) 2009 Charlie Poole
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // ***********************************************************************
23 
24 using System;
25 using System.Runtime.InteropServices;
26 
27 namespace NUnit.Framework.Constraints
28 {
29 
30     /// <summary>Helper routines for working with floating point numbers</summary>
31     /// <remarks>
32     ///   <para>
33     ///     The floating point comparison code is based on this excellent article:
34     ///     http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
35     ///   </para>
36     ///   <para>
37     ///     "ULP" means Unit in the Last Place and in the context of this library refers to
38     ///     the distance between two adjacent floating point numbers. IEEE floating point
39     ///     numbers can only represent a finite subset of natural numbers, with greater
40     ///     accuracy for smaller numbers and lower accuracy for very large numbers.
41     ///   </para>
42     ///   <para>
43     ///     If a comparison is allowed "2 ulps" of deviation, that means the values are
44     ///     allowed to deviate by up to 2 adjacent floating point values, which might be
45     ///     as low as 0.0000001 for small numbers or as high as 10.0 for large numbers.
46     ///   </para>
47     /// </remarks>
48     public class FloatingPointNumerics
49     {
50 
51         #region struct FloatIntUnion
52 
53         /// <summary>Union of a floating point variable and an integer</summary>
54         [StructLayout(LayoutKind.Explicit)]
55         private struct FloatIntUnion
56         {
57             /// <summary>The union's value as a floating point variable</summary>
58             [FieldOffset(0)]
59             public float Float;
60 
61             /// <summary>The union's value as an integer</summary>
62             [FieldOffset(0)]
63             public int Int;
64 
65             /// <summary>The union's value as an unsigned integer</summary>
66             [FieldOffset(0)]
67             public uint UInt;
68         }
69 
70         #endregion // struct FloatIntUnion
71 
72         #region struct DoubleLongUnion
73 
74         /// <summary>Union of a double precision floating point variable and a long</summary>
75         [StructLayout(LayoutKind.Explicit)]
76         private struct DoubleLongUnion
77         {
78             /// <summary>The union's value as a double precision floating point variable</summary>
79             [FieldOffset(0)]
80             public double Double;
81 
82             /// <summary>The union's value as a long</summary>
83             [FieldOffset(0)]
84             public long Long;
85 
86             /// <summary>The union's value as an unsigned long</summary>
87             [FieldOffset(0)]
88             public ulong ULong;
89         }
90 
91         #endregion // struct DoubleLongUnion
92 
93         /// <summary>Compares two floating point values for equality</summary>
94         /// <param name="left">First floating point value to be compared</param>
95         /// <param name="right">Second floating point value t be compared</param>
96         /// <param name="maxUlps">
97         ///   Maximum number of representable floating point values that are allowed to
98         ///   be between the left and the right floating point values
99         /// </param>
100         /// <returns>True if both numbers are equal or close to being equal</returns>
101         /// <remarks>
102         ///   <para>
103         ///     Floating point values can only represent a finite subset of natural numbers.
104         ///     For example, the values 2.00000000 and 2.00000024 can be stored in a float,
105         ///     but nothing inbetween them.
106         ///   </para>
107         ///   <para>
108         ///     This comparison will count how many possible floating point values are between
109         ///     the left and the right number. If the number of possible values between both
110         ///     numbers is less than or equal to maxUlps, then the numbers are considered as
111         ///     being equal.
112         ///   </para>
113         ///   <para>
114         ///     Implementation partially follows the code outlined here:
115         ///     http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/
116         ///   </para>
117         /// </remarks>
AreAlmostEqualUlps(float left, float right, int maxUlps)118         public static bool AreAlmostEqualUlps(float left, float right, int maxUlps)
119         {
120             FloatIntUnion leftUnion = new FloatIntUnion();
121             FloatIntUnion rightUnion = new FloatIntUnion();
122 
123             leftUnion.Float = left;
124             rightUnion.Float = right;
125 
126             uint leftSignMask = (leftUnion.UInt >> 31);
127             uint rightSignMask = (rightUnion.UInt >> 31);
128 
129             uint leftTemp = ((0x80000000 - leftUnion.UInt) & leftSignMask);
130             leftUnion.UInt = leftTemp | (leftUnion.UInt & ~leftSignMask);
131 
132             uint rightTemp = ((0x80000000 - rightUnion.UInt) & rightSignMask);
133             rightUnion.UInt = rightTemp | (rightUnion.UInt & ~rightSignMask);
134 
135             return (Math.Abs(leftUnion.Int - rightUnion.Int) <= maxUlps);
136         }
137 
138         /// <summary>Compares two double precision floating point values for equality</summary>
139         /// <param name="left">First double precision floating point value to be compared</param>
140         /// <param name="right">Second double precision floating point value t be compared</param>
141         /// <param name="maxUlps">
142         ///   Maximum number of representable double precision floating point values that are
143         ///   allowed to be between the left and the right double precision floating point values
144         /// </param>
145         /// <returns>True if both numbers are equal or close to being equal</returns>
146         /// <remarks>
147         ///   <para>
148         ///     Double precision floating point values can only represent a limited series of
149         ///     natural numbers. For example, the values 2.0000000000000000 and 2.0000000000000004
150         ///     can be stored in a double, but nothing inbetween them.
151         ///   </para>
152         ///   <para>
153         ///     This comparison will count how many possible double precision floating point
154         ///     values are between the left and the right number. If the number of possible
155         ///     values between both numbers is less than or equal to maxUlps, then the numbers
156         ///     are considered as being equal.
157         ///   </para>
158         ///   <para>
159         ///     Implementation partially follows the code outlined here:
160         ///     http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/
161         ///   </para>
162         /// </remarks>
AreAlmostEqualUlps(double left, double right, long maxUlps)163         public static bool AreAlmostEqualUlps(double left, double right, long maxUlps)
164         {
165             DoubleLongUnion leftUnion = new DoubleLongUnion();
166             DoubleLongUnion rightUnion = new DoubleLongUnion();
167 
168             leftUnion.Double = left;
169             rightUnion.Double = right;
170 
171             ulong leftSignMask = (leftUnion.ULong >> 63);
172             ulong rightSignMask = (rightUnion.ULong >> 63);
173 
174             ulong leftTemp = ((0x8000000000000000 - leftUnion.ULong) & leftSignMask);
175             leftUnion.ULong = leftTemp | (leftUnion.ULong & ~leftSignMask);
176 
177             ulong rightTemp = ((0x8000000000000000 - rightUnion.ULong) & rightSignMask);
178             rightUnion.ULong = rightTemp | (rightUnion.ULong & ~rightSignMask);
179 
180             return (Math.Abs(leftUnion.Long - rightUnion.Long) <= maxUlps);
181         }
182 
183         /// <summary>
184         ///   Reinterprets the memory contents of a floating point value as an integer value
185         /// </summary>
186         /// <param name="value">
187         ///   Floating point value whose memory contents to reinterpret
188         /// </param>
189         /// <returns>
190         ///   The memory contents of the floating point value interpreted as an integer
191         /// </returns>
ReinterpretAsInt(float value)192         public static int ReinterpretAsInt(float value)
193         {
194             FloatIntUnion union = new FloatIntUnion();
195             union.Float = value;
196             return union.Int;
197         }
198 
199         /// <summary>
200         ///   Reinterprets the memory contents of a double precision floating point
201         ///   value as an integer value
202         /// </summary>
203         /// <param name="value">
204         ///   Double precision floating point value whose memory contents to reinterpret
205         /// </param>
206         /// <returns>
207         ///   The memory contents of the double precision floating point value
208         ///   interpreted as an integer
209         /// </returns>
ReinterpretAsLong(double value)210         public static long ReinterpretAsLong(double value)
211         {
212             DoubleLongUnion union = new DoubleLongUnion();
213             union.Double = value;
214             return union.Long;
215         }
216 
217         /// <summary>
218         ///   Reinterprets the memory contents of an integer as a floating point value
219         /// </summary>
220         /// <param name="value">Integer value whose memory contents to reinterpret</param>
221         /// <returns>
222         ///   The memory contents of the integer value interpreted as a floating point value
223         /// </returns>
ReinterpretAsFloat(int value)224         public static float ReinterpretAsFloat(int value)
225         {
226             FloatIntUnion union = new FloatIntUnion();
227             union.Int = value;
228             return union.Float;
229         }
230 
231         /// <summary>
232         ///   Reinterprets the memory contents of an integer value as a double precision
233         ///   floating point value
234         /// </summary>
235         /// <param name="value">Integer whose memory contents to reinterpret</param>
236         /// <returns>
237         ///   The memory contents of the integer interpreted as a double precision
238         ///   floating point value
239         /// </returns>
ReinterpretAsDouble(long value)240         public static double ReinterpretAsDouble(long value)
241         {
242             DoubleLongUnion union = new DoubleLongUnion();
243             union.Long = value;
244             return union.Double;
245         }
246 
FloatingPointNumerics()247         private FloatingPointNumerics()
248         {
249         }
250     }
251 }
252