/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