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

Tolerance

public class Tolerance
The Tolerance class generalizes the notion of a tolerance within which an equality test succeeds. Normally, it is used with numeric types, but it can be used with any type that supports taking a difference between two objects and comparing that difference to a value.
using System; namespace NUnit.Framework.Constraints { public class Tolerance { public class Range { public readonly object LowerBound; public readonly object UpperBound; public Range(object lowerBound, object upperBound) { LowerBound = lowerBound; UpperBound = upperBound; } } private const string ModeMustFollowTolerance = "Tolerance amount must be specified before setting mode"; private const string MultipleToleranceModes = "Tried to use multiple tolerance modes at the same time"; private const string NumericToleranceRequired = "A numeric tolerance is required"; public static Tolerance Default => new Tolerance(0, ToleranceMode.Unset); public static Tolerance Exact => new Tolerance(0, ToleranceMode.Linear); public Tolerance Percent { get { CheckLinearAndNumeric(); return new Tolerance(Amount, ToleranceMode.Percent); } } public Tolerance Ulps { get { CheckLinearAndNumeric(); return new Tolerance(Amount, ToleranceMode.Ulps); } } public Tolerance Days { get { CheckLinearAndNumeric(); return new Tolerance(TimeSpan.FromDays(Convert.ToDouble(Amount))); } } public Tolerance Hours { get { CheckLinearAndNumeric(); return new Tolerance(TimeSpan.FromHours(Convert.ToDouble(Amount))); } } public Tolerance Minutes { get { CheckLinearAndNumeric(); return new Tolerance(TimeSpan.FromMinutes(Convert.ToDouble(Amount))); } } public Tolerance Seconds { get { CheckLinearAndNumeric(); return new Tolerance(TimeSpan.FromSeconds(Convert.ToDouble(Amount))); } } public Tolerance Milliseconds { get { CheckLinearAndNumeric(); return new Tolerance(TimeSpan.FromMilliseconds(Convert.ToDouble(Amount))); } } public Tolerance Ticks { get { CheckLinearAndNumeric(); return new Tolerance(TimeSpan.FromTicks(Convert.ToInt64(Amount))); } } public ToleranceMode Mode { get; } public object Amount { get; } public bool IsUnsetOrDefault => Mode == ToleranceMode.Unset; public Tolerance(object amount) : this(amount, ToleranceMode.Linear) { } private Tolerance(object amount, ToleranceMode mode) { Amount = amount; Mode = mode; } public Range ApplyToValue(object value) { switch (Mode) { default: return new Range(value, value); case ToleranceMode.Linear: return LinearRange(value); case ToleranceMode.Percent: return PercentRange(value); } } private void CheckLinearAndNumeric() { if (Mode != ToleranceMode.Linear) throw new InvalidOperationException((Mode == ToleranceMode.Unset) ? "Tolerance amount must be specified before setting mode" : "Tried to use multiple tolerance modes at the same time"); if (!Numerics.IsNumericType(Amount)) throw new InvalidOperationException("A numeric tolerance is required"); } private Range LinearRange(object value) { if (Amount is double || value is double) { double num = Convert.ToDouble(Amount); double num2 = Convert.ToDouble(value); return new Range(num2 - num, num2 + num); } if (Amount is float || value is float) { float num3 = Convert.ToSingle(Amount); float num4 = Convert.ToSingle(value); return new Range(num4 - num3, num4 + num3); } if (Amount is decimal || value is decimal) { decimal d = Convert.ToDecimal(Amount); decimal d2 = Convert.ToDecimal(value); return new Range(d2 - d, d2 + d); } if (Amount is ulong || value is ulong) { ulong num5 = Convert.ToUInt64(Amount); ulong num6 = Convert.ToUInt64(value); return new Range(num6 - num5, num6 + num5); } if (Amount is long || value is long) { long num7 = Convert.ToInt64(Amount); long num8 = Convert.ToInt64(value); return new Range(num8 - num7, num8 + num7); } if (Amount is uint || value is uint) { uint num9 = Convert.ToUInt32(Amount); uint num10 = Convert.ToUInt32(value); return new Range(num10 - num9, num10 + num9); } if (Numerics.IsFixedPointNumeric(Amount) && Numerics.IsFixedPointNumeric(value)) { int num11 = Convert.ToInt32(Amount); int num12 = Convert.ToInt32(value); return new Range(num12 - num11, num12 + num11); } throw new InvalidOperationException("Cannot create range for a non-numeric value"); } private Range PercentRange(object value) { if (!Numerics.IsNumericType(Amount) || !Numerics.IsNumericType(value)) throw new InvalidOperationException("Cannot create range for a non-numeric value"); double num = Convert.ToDouble(value); double num2 = num * Convert.ToDouble(Amount) / 100; return new Range(num - num2, num + num2); } } }