PageRenderTime 53ms CodeModel.GetById 12ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 0ms

/src/NUnit/framework/Constraints/Numerics.cs

#
C# | 363 lines | 250 code | 63 blank | 50 comment | 82 complexity | b5af39eb1a41612b4cffefafb57b5649 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
  7using System;
  8
  9namespace NUnit.Framework.Constraints
 10{
 11	/// <summary>
 12	/// The Numerics class contains common operations on numeric values.
 13	/// </summary>
 14	public class Numerics
 15	{
 16		#region Numeric Type Recognition
 17		/// <summary>
 18		/// Checks the type of the object, returning true if
 19		/// the object is a numeric type.
 20		/// </summary>
 21		/// <param name="obj">The object to check</param>
 22		/// <returns>true if the object is a numeric type</returns>
 23		public static bool IsNumericType(Object obj)
 24		{
 25			return IsFloatingPointNumeric( obj ) || IsFixedPointNumeric( obj );
 26		}
 27
 28		/// <summary>
 29		/// Checks the type of the object, returning true if
 30		/// the object is a floating point numeric type.
 31		/// </summary>
 32		/// <param name="obj">The object to check</param>
 33		/// <returns>true if the object is a floating point numeric type</returns>
 34		public static bool IsFloatingPointNumeric(Object obj)
 35		{
 36			if (null != obj)
 37			{
 38				if (obj is System.Double) return true;
 39				if (obj is System.Single) return true;
 40			}
 41			return false;
 42		}
 43		/// <summary>
 44		/// Checks the type of the object, returning true if
 45		/// the object is a fixed point numeric type.
 46		/// </summary>
 47		/// <param name="obj">The object to check</param>
 48		/// <returns>true if the object is a fixed point numeric type</returns>
 49		public static bool IsFixedPointNumeric(Object obj)
 50		{
 51			if (null != obj)
 52			{
 53				if (obj is System.Byte) return true;
 54				if (obj is System.SByte) return true;
 55				if (obj is System.Decimal) return true;
 56				if (obj is System.Int32) return true;
 57				if (obj is System.UInt32) return true;
 58				if (obj is System.Int64) return true;
 59				if (obj is System.UInt64) return true;
 60				if (obj is System.Int16) return true;
 61				if (obj is System.UInt16) return true;
 62			}
 63			return false;
 64		}
 65		#endregion
 66
 67		#region Numeric Equality
 68        /// <summary>
 69        /// Test two numeric values for equality, performing the usual numeric 
 70        /// conversions and using a provided or default tolerance. If the tolerance 
 71        /// provided is Empty, this method may set it to a default tolerance.
 72        /// </summary>
 73        /// <param name="expected">The expected value</param>
 74        /// <param name="actual">The actual value</param>
 75        /// <param name="tolerance">A reference to the tolerance in effect</param>
 76        /// <returns>True if the values are equal</returns>
 77		public static bool AreEqual( object expected, object actual, ref Tolerance tolerance )
 78		{
 79            if ( expected is double || actual is double )
 80                return AreEqual( Convert.ToDouble(expected), Convert.ToDouble(actual), ref tolerance );
 81
 82            if ( expected is float || actual is float )
 83                return AreEqual( Convert.ToSingle(expected), Convert.ToSingle(actual), ref tolerance );
 84
 85            if (tolerance.Mode == ToleranceMode.Ulps)
 86                throw new InvalidOperationException("Ulps may only be specified for floating point arguments");
 87
 88			if ( expected is decimal || actual is decimal )
 89				return AreEqual( Convert.ToDecimal(expected), Convert.ToDecimal(actual), tolerance );
 90
 91            if (expected is ulong || actual is ulong)
 92                return AreEqual(Convert.ToUInt64(expected), Convert.ToUInt64(actual), tolerance );
 93		
 94			if ( expected is long || actual is long )
 95				return AreEqual( Convert.ToInt64(expected), Convert.ToInt64(actual), tolerance );
 96			
 97			if ( expected is uint || actual is uint )
 98				return AreEqual( Convert.ToUInt32(expected), Convert.ToUInt32(actual), tolerance );
 99
100			return AreEqual( Convert.ToInt32(expected), Convert.ToInt32(actual), tolerance );
101		}
102
103        private static bool AreEqual( double expected, double actual, ref Tolerance tolerance )
104		{
105            if (double.IsNaN(expected) && double.IsNaN(actual))
106                return true;
107
108            // Handle infinity specially since subtracting two infinite values gives 
109            // NaN and the following test fails. mono also needs NaN to be handled
110            // specially although ms.net could use either method. Also, handle
111            // situation where no tolerance is used.
112            if (double.IsInfinity(expected) || double.IsNaN(expected) || double.IsNaN(actual))
113            {
114                return expected.Equals(actual);
115            }
116
117            if (tolerance.IsEmpty && GlobalSettings.DefaultFloatingPointTolerance > 0.0d)
118                tolerance = new Tolerance(GlobalSettings.DefaultFloatingPointTolerance);
119
120            switch (tolerance.Mode)
121            {
122                case ToleranceMode.None:
123                    return expected.Equals(actual);
124
125                case ToleranceMode.Linear:
126                    return Math.Abs(expected - actual) <= Convert.ToDouble(tolerance.Value);
127
128                case ToleranceMode.Percent:
129                    if (expected == 0.0)
130                        return expected.Equals(actual);
131                    
132                    double relativeError = Math.Abs((expected - actual) / expected);
133                    return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
134#if !NETCF_1_0
135                case ToleranceMode.Ulps:
136                    return FloatingPointNumerics.AreAlmostEqualUlps(
137                        expected, actual, Convert.ToInt64(tolerance.Value));
138#endif
139                default:
140                    throw new ArgumentException("Unknown tolerance mode specified", "mode");
141            }
142        }
143
144        private static bool AreEqual( float expected, float actual, ref Tolerance tolerance )
145		{
146            if ( float.IsNaN(expected) && float.IsNaN(actual) )
147                return true;
148
149            // handle infinity specially since subtracting two infinite values gives 
150            // NaN and the following test fails. mono also needs NaN to be handled
151            // specially although ms.net could use either method.
152            if (float.IsInfinity(expected) || float.IsNaN(expected) || float.IsNaN(actual))
153            {
154                return expected.Equals(actual);
155            }
156
157            if (tolerance.IsEmpty && GlobalSettings.DefaultFloatingPointTolerance > 0.0d)
158                tolerance = new Tolerance(GlobalSettings.DefaultFloatingPointTolerance);
159
160            switch (tolerance.Mode)
161            {
162                case ToleranceMode.None:
163                    return expected.Equals(actual);
164
165                case ToleranceMode.Linear:
166                    return Math.Abs(expected - actual) <= Convert.ToDouble(tolerance.Value);
167
168                case ToleranceMode.Percent:
169                    if (expected == 0.0f)
170                        return expected.Equals(actual);
171                    float relativeError = Math.Abs((expected - actual) / expected);
172                    return (relativeError <= Convert.ToSingle(tolerance.Value) / 100.0f);
173#if !NETCF_1_0
174                case ToleranceMode.Ulps:
175                    return FloatingPointNumerics.AreAlmostEqualUlps(
176                        expected, actual, Convert.ToInt32(tolerance.Value));
177#endif
178                default:
179                    throw new ArgumentException("Unknown tolerance mode specified", "mode");
180            }
181		}
182
183
184        private static bool AreEqual( decimal expected, decimal actual, Tolerance tolerance )
185        {
186            switch (tolerance.Mode)
187            {
188                case ToleranceMode.None:
189                    return expected.Equals(actual);
190
191                case ToleranceMode.Linear:
192                    decimal decimalTolerance = Convert.ToDecimal(tolerance.Value);
193                    if(decimalTolerance > 0m)
194                      return Math.Abs(expected - actual) <= decimalTolerance;
195				
196			        return expected.Equals( actual );
197
198                case ToleranceMode.Percent:
199                    if(expected == 0m)
200                        return expected.Equals(actual);
201
202                    double relativeError = Math.Abs(
203                        (double)(expected - actual) / (double)expected);
204                    return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
205
206                default:
207                    throw new ArgumentException("Unknown tolerance mode specified", "mode");
208            }
209        }
210
211		private static bool AreEqual( ulong expected, ulong actual, Tolerance tolerance )
212		{
213            switch (tolerance.Mode)
214            {
215                case ToleranceMode.None:
216                    return expected.Equals(actual);
217
218                case ToleranceMode.Linear:
219                    ulong ulongTolerance = Convert.ToUInt64(tolerance.Value);
220                    if(ulongTolerance > 0ul)
221			        {
222				        ulong diff = expected >= actual ? expected - actual : actual - expected;
223                        return diff <= ulongTolerance;
224			        }
225
226			        return expected.Equals( actual );
227
228                case ToleranceMode.Percent:
229                    if (expected == 0ul)
230                        return expected.Equals(actual);
231
232                    // Can't do a simple Math.Abs() here since it's unsigned
233                    ulong difference = Math.Max(expected, actual) - Math.Min(expected, actual);
234                    double relativeError = Math.Abs( (double)difference / (double)expected );
235                    return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
236
237                default:
238                    throw new ArgumentException("Unknown tolerance mode specified", "mode");
239            }
240		}
241
242		private static bool AreEqual( long expected, long actual, Tolerance tolerance )
243		{
244            switch (tolerance.Mode)
245            {
246                case ToleranceMode.None:
247                    return expected.Equals(actual);
248
249                case ToleranceMode.Linear:
250                    long longTolerance = Convert.ToInt64(tolerance.Value);
251                    if(longTolerance > 0L)
252				        return Math.Abs(expected - actual) <= longTolerance;
253
254			        return expected.Equals( actual );
255
256                case ToleranceMode.Percent:
257                    if(expected == 0L)
258                        return expected.Equals(actual);
259
260                    double relativeError = Math.Abs(
261                        (double)(expected - actual) / (double)expected);
262                    return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
263
264                default:
265                    throw new ArgumentException("Unknown tolerance mode specified", "mode");
266            }
267		}
268
269		private static bool AreEqual( uint expected, uint actual, Tolerance tolerance )
270		{
271            switch (tolerance.Mode)
272            {
273                case ToleranceMode.None:
274                    return expected.Equals(actual);
275
276                case ToleranceMode.Linear:
277                    uint uintTolerance = Convert.ToUInt32(tolerance.Value);
278                    if(uintTolerance > 0)
279			        {
280				        uint diff = expected >= actual ? expected - actual : actual - expected;
281                        return diff <= uintTolerance;
282			        }
283				
284			        return expected.Equals( actual );
285
286                case ToleranceMode.Percent:
287                    if(expected == 0u)
288                        return expected.Equals(actual);
289
290                    // Can't do a simple Math.Abs() here since it's unsigned
291                    uint difference = Math.Max(expected, actual) - Math.Min(expected, actual);
292                    double relativeError = Math.Abs((double)difference / (double)expected );
293                    return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
294
295                default:
296                    throw new ArgumentException("Unknown tolerance mode specified", "mode");
297            }
298		}
299
300		private static bool AreEqual( int expected, int actual, Tolerance tolerance )
301		{
302            switch (tolerance.Mode)
303            {
304                case ToleranceMode.None:
305                    return expected.Equals(actual);
306
307                case ToleranceMode.Linear:
308                    int intTolerance = Convert.ToInt32(tolerance.Value);
309                    if (intTolerance > 0)
310                        return Math.Abs(expected - actual) <= intTolerance;
311
312                    return expected.Equals(actual);
313
314                case ToleranceMode.Percent:
315                    if (expected == 0)
316                        return expected.Equals(actual);
317
318                    double relativeError = Math.Abs(
319                        (double)(expected - actual) / (double)expected);
320                    return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
321
322                default:
323                    throw new ArgumentException("Unknown tolerance mode specified", "mode");
324            }
325		}
326		#endregion
327
328		#region Numeric Comparisons 
329        /// <summary>
330        /// Compare two numeric values, performing the usual numeric conversions.
331        /// </summary>
332        /// <param name="expected">The expected value</param>
333        /// <param name="actual">The actual value</param>
334        /// <returns>The relationship of the values to each other</returns>
335		public static int Compare( object expected, object actual )
336		{
337			if( !IsNumericType( expected ) || !IsNumericType( actual ) )
338				throw new ArgumentException( "Both arguments must be numeric");
339
340			if ( IsFloatingPointNumeric(expected) || IsFloatingPointNumeric(actual) )
341				return Convert.ToDouble(expected).CompareTo(Convert.ToDouble(actual));
342
343			if ( expected is decimal || actual is decimal )
344				return Convert.ToDecimal(expected).CompareTo(Convert.ToDecimal(actual));
345		
346			if ( expected is ulong || actual is ulong )
347				return Convert.ToUInt64(expected).CompareTo(Convert.ToUInt64(actual));
348	
349			if ( expected is long || actual is long )
350				return Convert.ToInt64(expected).CompareTo(Convert.ToInt64(actual));
351		
352			if ( expected is uint || actual is uint )
353				return Convert.ToUInt32(expected).CompareTo(Convert.ToUInt32(actual));
354
355			return Convert.ToInt32(expected).CompareTo(Convert.ToInt32(actual));
356        }
357		#endregion
358
359		private Numerics()
360		{
361		}
362	}
363}