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);
}
}
}