/src/NUnit/framework/Constraints/Numerics.cs
C# | 363 lines | 250 code | 63 blank | 50 comment | 82 complexity | b5af39eb1a41612b4cffefafb57b5649 MD5 | raw file
Possible License(s): GPL-2.0
- // ****************************************************************
- // Copyright 2008, Charlie Poole
- // This is free software licensed under the NUnit license. You may
- // obtain a copy of the license at http://nunit.org
- // ****************************************************************
-
- using System;
-
- namespace NUnit.Framework.Constraints
- {
- /// <summary>
- /// The Numerics class contains common operations on numeric values.
- /// </summary>
- public class Numerics
- {
- #region Numeric Type Recognition
- /// <summary>
- /// Checks the type of the object, returning true if
- /// the object is a numeric type.
- /// </summary>
- /// <param name="obj">The object to check</param>
- /// <returns>true if the object is a numeric type</returns>
- public static bool IsNumericType(Object obj)
- {
- return IsFloatingPointNumeric( obj ) || IsFixedPointNumeric( obj );
- }
-
- /// <summary>
- /// Checks the type of the object, returning true if
- /// the object is a floating point numeric type.
- /// </summary>
- /// <param name="obj">The object to check</param>
- /// <returns>true if the object is a floating point numeric type</returns>
- public static bool IsFloatingPointNumeric(Object obj)
- {
- if (null != obj)
- {
- if (obj is System.Double) return true;
- if (obj is System.Single) return true;
- }
- return false;
- }
- /// <summary>
- /// Checks the type of the object, returning true if
- /// the object is a fixed point numeric type.
- /// </summary>
- /// <param name="obj">The object to check</param>
- /// <returns>true if the object is a fixed point numeric type</returns>
- public static bool IsFixedPointNumeric(Object obj)
- {
- if (null != obj)
- {
- if (obj is System.Byte) return true;
- if (obj is System.SByte) return true;
- if (obj is System.Decimal) return true;
- if (obj is System.Int32) return true;
- if (obj is System.UInt32) return true;
- if (obj is System.Int64) return true;
- if (obj is System.UInt64) return true;
- if (obj is System.Int16) return true;
- if (obj is System.UInt16) return true;
- }
- return false;
- }
- #endregion
-
- #region Numeric Equality
- /// <summary>
- /// Test two numeric values for equality, performing the usual numeric
- /// conversions and using a provided or default tolerance. If the tolerance
- /// provided is Empty, this method may set it to a default tolerance.
- /// </summary>
- /// <param name="expected">The expected value</param>
- /// <param name="actual">The actual value</param>
- /// <param name="tolerance">A reference to the tolerance in effect</param>
- /// <returns>True if the values are equal</returns>
- public static bool AreEqual( object expected, object actual, ref Tolerance tolerance )
- {
- if ( expected is double || actual is double )
- return AreEqual( Convert.ToDouble(expected), Convert.ToDouble(actual), ref tolerance );
-
- if ( expected is float || actual is float )
- return AreEqual( Convert.ToSingle(expected), Convert.ToSingle(actual), ref tolerance );
-
- if (tolerance.Mode == ToleranceMode.Ulps)
- throw new InvalidOperationException("Ulps may only be specified for floating point arguments");
-
- if ( expected is decimal || actual is decimal )
- return AreEqual( Convert.ToDecimal(expected), Convert.ToDecimal(actual), tolerance );
-
- if (expected is ulong || actual is ulong)
- return AreEqual(Convert.ToUInt64(expected), Convert.ToUInt64(actual), tolerance );
-
- if ( expected is long || actual is long )
- return AreEqual( Convert.ToInt64(expected), Convert.ToInt64(actual), tolerance );
-
- if ( expected is uint || actual is uint )
- return AreEqual( Convert.ToUInt32(expected), Convert.ToUInt32(actual), tolerance );
-
- return AreEqual( Convert.ToInt32(expected), Convert.ToInt32(actual), tolerance );
- }
-
- private static bool AreEqual( double expected, double actual, ref Tolerance tolerance )
- {
- if (double.IsNaN(expected) && double.IsNaN(actual))
- return true;
-
- // Handle infinity specially since subtracting two infinite values gives
- // NaN and the following test fails. mono also needs NaN to be handled
- // specially although ms.net could use either method. Also, handle
- // situation where no tolerance is used.
- if (double.IsInfinity(expected) || double.IsNaN(expected) || double.IsNaN(actual))
- {
- return expected.Equals(actual);
- }
-
- if (tolerance.IsEmpty && GlobalSettings.DefaultFloatingPointTolerance > 0.0d)
- tolerance = new Tolerance(GlobalSettings.DefaultFloatingPointTolerance);
-
- switch (tolerance.Mode)
- {
- case ToleranceMode.None:
- return expected.Equals(actual);
-
- case ToleranceMode.Linear:
- return Math.Abs(expected - actual) <= Convert.ToDouble(tolerance.Value);
-
- case ToleranceMode.Percent:
- if (expected == 0.0)
- return expected.Equals(actual);
-
- double relativeError = Math.Abs((expected - actual) / expected);
- return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
- #if !NETCF_1_0
- case ToleranceMode.Ulps:
- return FloatingPointNumerics.AreAlmostEqualUlps(
- expected, actual, Convert.ToInt64(tolerance.Value));
- #endif
- default:
- throw new ArgumentException("Unknown tolerance mode specified", "mode");
- }
- }
-
- private static bool AreEqual( float expected, float actual, ref Tolerance tolerance )
- {
- if ( float.IsNaN(expected) && float.IsNaN(actual) )
- return true;
-
- // handle infinity specially since subtracting two infinite values gives
- // NaN and the following test fails. mono also needs NaN to be handled
- // specially although ms.net could use either method.
- if (float.IsInfinity(expected) || float.IsNaN(expected) || float.IsNaN(actual))
- {
- return expected.Equals(actual);
- }
-
- if (tolerance.IsEmpty && GlobalSettings.DefaultFloatingPointTolerance > 0.0d)
- tolerance = new Tolerance(GlobalSettings.DefaultFloatingPointTolerance);
-
- switch (tolerance.Mode)
- {
- case ToleranceMode.None:
- return expected.Equals(actual);
-
- case ToleranceMode.Linear:
- return Math.Abs(expected - actual) <= Convert.ToDouble(tolerance.Value);
-
- case ToleranceMode.Percent:
- if (expected == 0.0f)
- return expected.Equals(actual);
- float relativeError = Math.Abs((expected - actual) / expected);
- return (relativeError <= Convert.ToSingle(tolerance.Value) / 100.0f);
- #if !NETCF_1_0
- case ToleranceMode.Ulps:
- return FloatingPointNumerics.AreAlmostEqualUlps(
- expected, actual, Convert.ToInt32(tolerance.Value));
- #endif
- default:
- throw new ArgumentException("Unknown tolerance mode specified", "mode");
- }
- }
-
-
- private static bool AreEqual( decimal expected, decimal actual, Tolerance tolerance )
- {
- switch (tolerance.Mode)
- {
- case ToleranceMode.None:
- return expected.Equals(actual);
-
- case ToleranceMode.Linear:
- decimal decimalTolerance = Convert.ToDecimal(tolerance.Value);
- if(decimalTolerance > 0m)
- return Math.Abs(expected - actual) <= decimalTolerance;
-
- return expected.Equals( actual );
-
- case ToleranceMode.Percent:
- if(expected == 0m)
- return expected.Equals(actual);
-
- double relativeError = Math.Abs(
- (double)(expected - actual) / (double)expected);
- return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
-
- default:
- throw new ArgumentException("Unknown tolerance mode specified", "mode");
- }
- }
-
- private static bool AreEqual( ulong expected, ulong actual, Tolerance tolerance )
- {
- switch (tolerance.Mode)
- {
- case ToleranceMode.None:
- return expected.Equals(actual);
-
- case ToleranceMode.Linear:
- ulong ulongTolerance = Convert.ToUInt64(tolerance.Value);
- if(ulongTolerance > 0ul)
- {
- ulong diff = expected >= actual ? expected - actual : actual - expected;
- return diff <= ulongTolerance;
- }
-
- return expected.Equals( actual );
-
- case ToleranceMode.Percent:
- if (expected == 0ul)
- return expected.Equals(actual);
-
- // Can't do a simple Math.Abs() here since it's unsigned
- ulong difference = Math.Max(expected, actual) - Math.Min(expected, actual);
- double relativeError = Math.Abs( (double)difference / (double)expected );
- return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
-
- default:
- throw new ArgumentException("Unknown tolerance mode specified", "mode");
- }
- }
-
- private static bool AreEqual( long expected, long actual, Tolerance tolerance )
- {
- switch (tolerance.Mode)
- {
- case ToleranceMode.None:
- return expected.Equals(actual);
-
- case ToleranceMode.Linear:
- long longTolerance = Convert.ToInt64(tolerance.Value);
- if(longTolerance > 0L)
- return Math.Abs(expected - actual) <= longTolerance;
-
- return expected.Equals( actual );
-
- case ToleranceMode.Percent:
- if(expected == 0L)
- return expected.Equals(actual);
-
- double relativeError = Math.Abs(
- (double)(expected - actual) / (double)expected);
- return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
-
- default:
- throw new ArgumentException("Unknown tolerance mode specified", "mode");
- }
- }
-
- private static bool AreEqual( uint expected, uint actual, Tolerance tolerance )
- {
- switch (tolerance.Mode)
- {
- case ToleranceMode.None:
- return expected.Equals(actual);
-
- case ToleranceMode.Linear:
- uint uintTolerance = Convert.ToUInt32(tolerance.Value);
- if(uintTolerance > 0)
- {
- uint diff = expected >= actual ? expected - actual : actual - expected;
- return diff <= uintTolerance;
- }
-
- return expected.Equals( actual );
-
- case ToleranceMode.Percent:
- if(expected == 0u)
- return expected.Equals(actual);
-
- // Can't do a simple Math.Abs() here since it's unsigned
- uint difference = Math.Max(expected, actual) - Math.Min(expected, actual);
- double relativeError = Math.Abs((double)difference / (double)expected );
- return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
-
- default:
- throw new ArgumentException("Unknown tolerance mode specified", "mode");
- }
- }
-
- private static bool AreEqual( int expected, int actual, Tolerance tolerance )
- {
- switch (tolerance.Mode)
- {
- case ToleranceMode.None:
- return expected.Equals(actual);
-
- case ToleranceMode.Linear:
- int intTolerance = Convert.ToInt32(tolerance.Value);
- if (intTolerance > 0)
- return Math.Abs(expected - actual) <= intTolerance;
-
- return expected.Equals(actual);
-
- case ToleranceMode.Percent:
- if (expected == 0)
- return expected.Equals(actual);
-
- double relativeError = Math.Abs(
- (double)(expected - actual) / (double)expected);
- return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
-
- default:
- throw new ArgumentException("Unknown tolerance mode specified", "mode");
- }
- }
- #endregion
-
- #region Numeric Comparisons
- /// <summary>
- /// Compare two numeric values, performing the usual numeric conversions.
- /// </summary>
- /// <param name="expected">The expected value</param>
- /// <param name="actual">The actual value</param>
- /// <returns>The relationship of the values to each other</returns>
- public static int Compare( object expected, object actual )
- {
- if( !IsNumericType( expected ) || !IsNumericType( actual ) )
- throw new ArgumentException( "Both arguments must be numeric");
-
- if ( IsFloatingPointNumeric(expected) || IsFloatingPointNumeric(actual) )
- return Convert.ToDouble(expected).CompareTo(Convert.ToDouble(actual));
-
- if ( expected is decimal || actual is decimal )
- return Convert.ToDecimal(expected).CompareTo(Convert.ToDecimal(actual));
-
- if ( expected is ulong || actual is ulong )
- return Convert.ToUInt64(expected).CompareTo(Convert.ToUInt64(actual));
-
- if ( expected is long || actual is long )
- return Convert.ToInt64(expected).CompareTo(Convert.ToInt64(actual));
-
- if ( expected is uint || actual is uint )
- return Convert.ToUInt32(expected).CompareTo(Convert.ToUInt32(actual));
-
- return Convert.ToInt32(expected).CompareTo(Convert.ToInt32(actual));
- }
- #endregion
-
- private Numerics()
- {
- }
- }
- }