Numerics
The Numerics class contains common operations on numeric values.
using NUnit.Framework.Internal;
using System;
namespace NUnit.Framework.Constraints
{
public static class Numerics
{
public static bool IsNumericType(object obj)
{
if (!IsFloatingPointNumeric(obj))
return IsFixedPointNumeric(obj);
return true;
}
public static bool IsFloatingPointNumeric(object obj)
{
if (obj != null) {
if (obj is double)
return true;
if (obj is float)
return true;
}
return false;
}
public static bool IsFixedPointNumeric(object obj)
{
if (obj != null) {
if (obj is byte)
return true;
if (obj is sbyte)
return true;
if (obj is decimal)
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;
}
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;
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);
return Math.Abs((expected - actual) / expected) <= 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);
return Math.Abs((expected - actual) / expected) <= 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 num = Convert.ToDecimal(tolerance.Amount);
if (num > decimal.Zero)
return Math.Abs(expected - actual) <= num;
return expected.Equals(actual);
}
case ToleranceMode.Percent:
if (expected == decimal.Zero)
return expected.Equals(actual);
return Math.Abs((double)(expected - actual) / (double)expected) <= 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 num = Convert.ToUInt64(tolerance.Amount);
if (num != 0)
return ((expected >= actual) ? (expected - actual) : (actual - expected)) <= num;
return expected.Equals(actual);
}
case ToleranceMode.Percent:
if (expected == 0)
return expected.Equals(actual);
return Math.Abs((double)(Math.Max(expected, actual) - Math.Min(expected, actual)) / (double)expected) <= 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 num = Convert.ToInt64(tolerance.Amount);
if (num > 0)
return Math.Abs(expected - actual) <= num;
return expected.Equals(actual);
}
case ToleranceMode.Percent:
if (expected == 0)
return expected.Equals(actual);
return Math.Abs((double)(expected - actual) / (double)expected) <= 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 num = Convert.ToUInt32(tolerance.Amount);
if (num != 0)
return ((expected >= actual) ? (expected - actual) : (actual - expected)) <= num;
return expected.Equals(actual);
}
case ToleranceMode.Percent:
if (expected == 0)
return expected.Equals(actual);
return Math.Abs((double)(Math.Max(expected, actual) - Math.Min(expected, actual)) / (double)expected) <= 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 num = Convert.ToInt32(tolerance.Amount);
if (num > 0)
return Math.Abs(expected - actual) <= num;
return expected.Equals(actual);
}
case ToleranceMode.Percent:
if (expected == 0)
return expected.Equals(actual);
return Math.Abs((double)(expected - actual) / (double)expected) <= 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");
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));
}
}
}