<PackageReference Include="NUnit" Version="4.3.0" />

Numerics

static class Numerics
The Numerics class contains common operations on numeric values.
using NUnit.Framework.Internal; using System; using System.Runtime.CompilerServices; namespace NUnit.Framework.Constraints { [NullableContext(1)] [Nullable(0)] internal static class Numerics { [NullableContext(2)] public static bool IsNumericType(object obj) { if (!IsFloatingPointNumeric(obj)) return IsFixedPointNumeric(obj); return true; } internal static bool IsNumericType(Type type) { if (!IsFloatingPointNumeric(type)) return IsFixedPointNumeric(type); return true; } [NullableContext(2)] public static bool IsFloatingPointNumeric(object obj) { if (obj != null) { if (obj is double) return true; if (obj is float) return true; if (obj is decimal) return true; } return false; } internal static bool IsFloatingPointNumeric(Type type) { if ((object)type != null) { if (type == typeof(double)) return true; if (type == typeof(float)) return true; if (type == typeof(decimal)) return true; } return false; } [NullableContext(2)] public static bool IsFixedPointNumeric(object obj) { if (obj != null) { if (obj is byte) return true; if (obj is sbyte) return true; if (obj is int) return true; if (obj is uint) return true; if (obj is long) return true; if (obj is ulong) return true; if (obj is short) return true; if (obj is ushort) return true; if (obj is char) return true; } return false; } internal static bool IsFixedPointNumeric(Type type) { if ((object)type != null) { if (type == typeof(byte)) return true; if (type == typeof(sbyte)) return true; if (type == typeof(int)) return true; if (type == typeof(uint)) return true; if (type == typeof(long)) return true; if (type == typeof(ulong)) return true; if (type == typeof(short)) return true; if (type == typeof(ushort)) return true; if (type == typeof(char)) return true; } return false; } private static bool IsWithinDecimalRange(double value) { if (value >= -7.922816251426434E+28) return value <= 7.922816251426434E+28; return false; } 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); } [NullableContext(0)] public static bool AreEqual<[IsUnmanaged] T1, [IsUnmanaged] T2>(T1 expected, T2 actual, [Nullable(1)] ref Tolerance tolerance) where T1 : struct, IConvertible where T2 : struct, IConvertible { if (((object)expected) is double || ((object)actual) is double) return AreEqual(expected.ToDouble(null), actual.ToDouble(null), ref tolerance); if (((object)expected) is float || ((object)actual) is float) return AreEqual(expected.ToSingle(null), actual.ToSingle(null), ref tolerance); if (tolerance.Mode == ToleranceMode.Ulps) throw new InvalidOperationException("Ulps may only be specified for floating point arguments"); if (((object)expected) is decimal || ((object)actual) is decimal) return AreEqual(expected.ToDecimal(null), actual.ToDecimal(null), tolerance); if (((object)expected) is ulong || ((object)actual) is ulong) return AreEqual(expected.ToUInt64(null), actual.ToUInt64(null), tolerance); if (((object)expected) is long || ((object)actual) is long) return AreEqual(expected.ToInt64(null), actual.ToInt64(null), tolerance); if (((object)expected) is uint || ((object)actual) is uint) return AreEqual(expected.ToUInt32(null), actual.ToUInt32(null), tolerance); return AreEqual(expected.ToInt32(null), actual.ToInt32(null), tolerance); } private static bool AreEqual(double expected, double actual, ref Tolerance tolerance) { if (double.IsNaN(expected) && double.IsNaN(actual)) return true; if (!double.IsInfinity(expected) && !double.IsNaN(expected) && !double.IsNaN(actual)) { if (tolerance.IsUnsetOrDefault) { Tolerance tolerance2 = TestExecutionContext.CurrentContext?.DefaultFloatingPointTolerance; if (tolerance2 != null && !tolerance2.IsUnsetOrDefault) tolerance = tolerance2; } switch (tolerance.Mode) { case ToleranceMode.Unset: return expected.Equals(actual); case ToleranceMode.Linear: return Math.Abs(expected - actual) <= Convert.ToDouble(tolerance.Amount); case ToleranceMode.Percent: { if (expected == 0) return expected.Equals(actual); double num = Math.Abs((expected - actual) / expected); return num <= Convert.ToDouble(tolerance.Amount) / 100; } case ToleranceMode.Ulps: return FloatingPointNumerics.AreAlmostEqualUlps(expected, actual, Convert.ToInt64(tolerance.Amount)); default: throw new ArgumentException("Unknown tolerance mode specified", "mode"); } } return expected.Equals(actual); } private static bool AreEqual(float expected, float actual, ref Tolerance tolerance) { if (float.IsNaN(expected) && float.IsNaN(actual)) return true; if (!float.IsInfinity(expected) && !float.IsNaN(expected) && !float.IsNaN(actual)) { if (tolerance.IsUnsetOrDefault) { Tolerance tolerance2 = TestExecutionContext.CurrentContext?.DefaultFloatingPointTolerance; if (tolerance2 != null && !tolerance2.IsUnsetOrDefault) tolerance = tolerance2; } switch (tolerance.Mode) { case ToleranceMode.Unset: return expected.Equals(actual); case ToleranceMode.Linear: return (double)Math.Abs(expected - actual) <= Convert.ToDouble(tolerance.Amount); case ToleranceMode.Percent: { if (expected == 0) return expected.Equals(actual); float num = Math.Abs((expected - actual) / expected); return num <= Convert.ToSingle(tolerance.Amount) / 100; } case ToleranceMode.Ulps: return FloatingPointNumerics.AreAlmostEqualUlps(expected, actual, Convert.ToInt32(tolerance.Amount)); default: throw new ArgumentException("Unknown tolerance mode specified", "mode"); } } return expected.Equals(actual); } private static bool AreEqual(decimal expected, decimal actual, Tolerance tolerance) { switch (tolerance.Mode) { case ToleranceMode.Unset: return expected.Equals(actual); case ToleranceMode.Linear: { decimal num2 = Convert.ToDecimal(tolerance.Amount); if (num2 > decimal.Zero) return Math.Abs(expected - actual) <= num2; return expected.Equals(actual); } case ToleranceMode.Percent: { if (expected == decimal.Zero) return expected.Equals(actual); double num = Math.Abs((double)(expected - actual) / (double)expected); return num <= Convert.ToDouble(tolerance.Amount) / 100; } default: throw new ArgumentException("Unknown tolerance mode specified", "mode"); } } private static bool AreEqual(ulong expected, ulong actual, Tolerance tolerance) { switch (tolerance.Mode) { case ToleranceMode.Unset: return expected.Equals(actual); case ToleranceMode.Linear: { ulong num3 = Convert.ToUInt64(tolerance.Amount); if (num3 != 0) { ulong num4 = (expected >= actual) ? (expected - actual) : (actual - expected); return num4 <= num3; } return expected.Equals(actual); } case ToleranceMode.Percent: { if (expected == 0) return expected.Equals(actual); ulong num = Math.Max(expected, actual) - Math.Min(expected, actual); double num2 = Math.Abs((double)num / (double)expected); return num2 <= Convert.ToDouble(tolerance.Amount) / 100; } default: throw new ArgumentException("Unknown tolerance mode specified", "mode"); } } private static bool AreEqual(long expected, long actual, Tolerance tolerance) { switch (tolerance.Mode) { case ToleranceMode.Unset: return expected.Equals(actual); case ToleranceMode.Linear: { long num2 = Convert.ToInt64(tolerance.Amount); if (num2 > 0) return Math.Abs(expected - actual) <= num2; return expected.Equals(actual); } case ToleranceMode.Percent: { if (expected == 0) return expected.Equals(actual); double num = Math.Abs((double)(expected - actual) / (double)expected); return num <= Convert.ToDouble(tolerance.Amount) / 100; } default: throw new ArgumentException("Unknown tolerance mode specified", "mode"); } } private static bool AreEqual(uint expected, uint actual, Tolerance tolerance) { switch (tolerance.Mode) { case ToleranceMode.Unset: return expected.Equals(actual); case ToleranceMode.Linear: { uint num3 = Convert.ToUInt32(tolerance.Amount); if (num3 != 0) { uint num4 = (expected >= actual) ? (expected - actual) : (actual - expected); return num4 <= num3; } return expected.Equals(actual); } case ToleranceMode.Percent: { if (expected == 0) return expected.Equals(actual); double num = (double)(Math.Max(expected, actual) - Math.Min(expected, actual)); double num2 = Math.Abs(num / (double)expected); return num2 <= Convert.ToDouble(tolerance.Amount) / 100; } default: throw new ArgumentException("Unknown tolerance mode specified", "mode"); } } private static bool AreEqual(int expected, int actual, Tolerance tolerance) { switch (tolerance.Mode) { case ToleranceMode.Unset: return expected.Equals(actual); case ToleranceMode.Linear: { int num2 = Convert.ToInt32(tolerance.Amount); if (num2 > 0) return Math.Abs(expected - actual) <= num2; return expected.Equals(actual); } case ToleranceMode.Percent: { if (expected == 0) return expected.Equals(actual); double num = Math.Abs((double)(expected - actual) / (double)expected); return num <= Convert.ToDouble(tolerance.Amount) / 100; } default: throw new ArgumentException("Unknown tolerance mode specified", "mode"); } } public static int Compare(object expected, object actual) { if (!IsNumericType(expected) || !IsNumericType(actual)) throw new ArgumentException("Both arguments must be numeric"); double num2; if (expected is decimal || actual is decimal) { if (expected is decimal) { decimal num = (decimal)expected; if (IsWithinDecimalRange(Convert.ToDouble(actual))) return num.CompareTo(Convert.ToDecimal(actual)); } if (actual is decimal) { decimal value = (decimal)actual; if (IsWithinDecimalRange(Convert.ToDouble(expected))) return Convert.ToDecimal(expected).CompareTo(value); } num2 = Convert.ToDouble(expected); return num2.CompareTo(Convert.ToDouble(actual)); } if (expected is double || actual is double) { num2 = Convert.ToDouble(expected); return num2.CompareTo(Convert.ToDouble(actual)); } if (expected is float || actual is float) return Convert.ToSingle(expected).CompareTo(Convert.ToSingle(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)); } [NullableContext(2)] [return: Nullable(1)] internal static object Difference(object expected, object actual, ToleranceMode toleranceMode) { switch (toleranceMode) { case ToleranceMode.Linear: return Difference(expected, actual, true); case ToleranceMode.Percent: return Difference(expected, actual, false); default: throw new InvalidOperationException("Cannot calculate a difference for specified tolerance mode"); } } [NullableContext(2)] [return: Nullable(1)] private static object Difference(object expected, object actual, bool isAbsolute) { if (!IsNumericType(expected) || !IsNumericType(actual)) return NaN; if (expected is decimal) { decimal num = (decimal)expected; if (IsWithinDecimalRange(Convert.ToDouble(actual))) { decimal num2 = num - Convert.ToDecimal(actual); return isAbsolute ? num2 : (num2 / num * 100); } } if (actual is decimal) { decimal d = (decimal)actual; if (IsWithinDecimalRange(Convert.ToDouble(expected))) { decimal num3 = Convert.ToDecimal(expected) - d; return isAbsolute ? num3 : (num3 / Convert.ToDecimal(expected) * 100); } } if (IsFloatingPointNumeric(expected) || IsFloatingPointNumeric(actual)) { double num4 = Convert.ToDouble(expected) - Convert.ToDouble(actual); return isAbsolute ? num4 : (num4 / Convert.ToDouble(expected) * 100); } if (expected is ulong || actual is ulong) { ulong num5 = Convert.ToUInt64(expected) - Convert.ToUInt64(actual); return isAbsolute ? ((double)num5) : ((double)num5 / (double)Convert.ToUInt64(expected) * 100); } if (expected is long || actual is long) { long num6 = Convert.ToInt64(expected) - Convert.ToInt64(actual); return isAbsolute ? ((double)num6) : ((double)num6 / (double)Convert.ToInt64(expected) * 100); } if (expected is uint || actual is uint) { uint num7 = Convert.ToUInt32(expected) - Convert.ToUInt32(actual); return isAbsolute ? ((double)num7) : ((double)num7 / (double)Convert.ToUInt32(expected) * 100); } int num8 = Convert.ToInt32(expected) - Convert.ToInt32(actual); return isAbsolute ? ((double)num8) : ((double)num8 / (double)Convert.ToInt32(expected) * 100); } } }