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