PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/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
  1. // ****************************************************************
  2. // Copyright 2008, Charlie Poole
  3. // This is free software licensed under the NUnit license. You may
  4. // obtain a copy of the license at http://nunit.org
  5. // ****************************************************************
  6. using System;
  7. namespace NUnit.Framework.Constraints
  8. {
  9. /// <summary>
  10. /// The Numerics class contains common operations on numeric values.
  11. /// </summary>
  12. public class Numerics
  13. {
  14. #region Numeric Type Recognition
  15. /// <summary>
  16. /// Checks the type of the object, returning true if
  17. /// the object is a numeric type.
  18. /// </summary>
  19. /// <param name="obj">The object to check</param>
  20. /// <returns>true if the object is a numeric type</returns>
  21. public static bool IsNumericType(Object obj)
  22. {
  23. return IsFloatingPointNumeric( obj ) || IsFixedPointNumeric( obj );
  24. }
  25. /// <summary>
  26. /// Checks the type of the object, returning true if
  27. /// the object is a floating point numeric type.
  28. /// </summary>
  29. /// <param name="obj">The object to check</param>
  30. /// <returns>true if the object is a floating point numeric type</returns>
  31. public static bool IsFloatingPointNumeric(Object obj)
  32. {
  33. if (null != obj)
  34. {
  35. if (obj is System.Double) return true;
  36. if (obj is System.Single) return true;
  37. }
  38. return false;
  39. }
  40. /// <summary>
  41. /// Checks the type of the object, returning true if
  42. /// the object is a fixed point numeric type.
  43. /// </summary>
  44. /// <param name="obj">The object to check</param>
  45. /// <returns>true if the object is a fixed point numeric type</returns>
  46. public static bool IsFixedPointNumeric(Object obj)
  47. {
  48. if (null != obj)
  49. {
  50. if (obj is System.Byte) return true;
  51. if (obj is System.SByte) return true;
  52. if (obj is System.Decimal) return true;
  53. if (obj is System.Int32) return true;
  54. if (obj is System.UInt32) return true;
  55. if (obj is System.Int64) return true;
  56. if (obj is System.UInt64) return true;
  57. if (obj is System.Int16) return true;
  58. if (obj is System.UInt16) return true;
  59. }
  60. return false;
  61. }
  62. #endregion
  63. #region Numeric Equality
  64. /// <summary>
  65. /// Test two numeric values for equality, performing the usual numeric
  66. /// conversions and using a provided or default tolerance. If the tolerance
  67. /// provided is Empty, this method may set it to a default tolerance.
  68. /// </summary>
  69. /// <param name="expected">The expected value</param>
  70. /// <param name="actual">The actual value</param>
  71. /// <param name="tolerance">A reference to the tolerance in effect</param>
  72. /// <returns>True if the values are equal</returns>
  73. public static bool AreEqual( object expected, object actual, ref Tolerance tolerance )
  74. {
  75. if ( expected is double || actual is double )
  76. return AreEqual( Convert.ToDouble(expected), Convert.ToDouble(actual), ref tolerance );
  77. if ( expected is float || actual is float )
  78. return AreEqual( Convert.ToSingle(expected), Convert.ToSingle(actual), ref tolerance );
  79. if (tolerance.Mode == ToleranceMode.Ulps)
  80. throw new InvalidOperationException("Ulps may only be specified for floating point arguments");
  81. if ( expected is decimal || actual is decimal )
  82. return AreEqual( Convert.ToDecimal(expected), Convert.ToDecimal(actual), tolerance );
  83. if (expected is ulong || actual is ulong)
  84. return AreEqual(Convert.ToUInt64(expected), Convert.ToUInt64(actual), tolerance );
  85. if ( expected is long || actual is long )
  86. return AreEqual( Convert.ToInt64(expected), Convert.ToInt64(actual), tolerance );
  87. if ( expected is uint || actual is uint )
  88. return AreEqual( Convert.ToUInt32(expected), Convert.ToUInt32(actual), tolerance );
  89. return AreEqual( Convert.ToInt32(expected), Convert.ToInt32(actual), tolerance );
  90. }
  91. private static bool AreEqual( double expected, double actual, ref Tolerance tolerance )
  92. {
  93. if (double.IsNaN(expected) && double.IsNaN(actual))
  94. return true;
  95. // Handle infinity specially since subtracting two infinite values gives
  96. // NaN and the following test fails. mono also needs NaN to be handled
  97. // specially although ms.net could use either method. Also, handle
  98. // situation where no tolerance is used.
  99. if (double.IsInfinity(expected) || double.IsNaN(expected) || double.IsNaN(actual))
  100. {
  101. return expected.Equals(actual);
  102. }
  103. if (tolerance.IsEmpty && GlobalSettings.DefaultFloatingPointTolerance > 0.0d)
  104. tolerance = new Tolerance(GlobalSettings.DefaultFloatingPointTolerance);
  105. switch (tolerance.Mode)
  106. {
  107. case ToleranceMode.None:
  108. return expected.Equals(actual);
  109. case ToleranceMode.Linear:
  110. return Math.Abs(expected - actual) <= Convert.ToDouble(tolerance.Value);
  111. case ToleranceMode.Percent:
  112. if (expected == 0.0)
  113. return expected.Equals(actual);
  114. double relativeError = Math.Abs((expected - actual) / expected);
  115. return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
  116. #if !NETCF_1_0
  117. case ToleranceMode.Ulps:
  118. return FloatingPointNumerics.AreAlmostEqualUlps(
  119. expected, actual, Convert.ToInt64(tolerance.Value));
  120. #endif
  121. default:
  122. throw new ArgumentException("Unknown tolerance mode specified", "mode");
  123. }
  124. }
  125. private static bool AreEqual( float expected, float actual, ref Tolerance tolerance )
  126. {
  127. if ( float.IsNaN(expected) && float.IsNaN(actual) )
  128. return true;
  129. // handle infinity specially since subtracting two infinite values gives
  130. // NaN and the following test fails. mono also needs NaN to be handled
  131. // specially although ms.net could use either method.
  132. if (float.IsInfinity(expected) || float.IsNaN(expected) || float.IsNaN(actual))
  133. {
  134. return expected.Equals(actual);
  135. }
  136. if (tolerance.IsEmpty && GlobalSettings.DefaultFloatingPointTolerance > 0.0d)
  137. tolerance = new Tolerance(GlobalSettings.DefaultFloatingPointTolerance);
  138. switch (tolerance.Mode)
  139. {
  140. case ToleranceMode.None:
  141. return expected.Equals(actual);
  142. case ToleranceMode.Linear:
  143. return Math.Abs(expected - actual) <= Convert.ToDouble(tolerance.Value);
  144. case ToleranceMode.Percent:
  145. if (expected == 0.0f)
  146. return expected.Equals(actual);
  147. float relativeError = Math.Abs((expected - actual) / expected);
  148. return (relativeError <= Convert.ToSingle(tolerance.Value) / 100.0f);
  149. #if !NETCF_1_0
  150. case ToleranceMode.Ulps:
  151. return FloatingPointNumerics.AreAlmostEqualUlps(
  152. expected, actual, Convert.ToInt32(tolerance.Value));
  153. #endif
  154. default:
  155. throw new ArgumentException("Unknown tolerance mode specified", "mode");
  156. }
  157. }
  158. private static bool AreEqual( decimal expected, decimal actual, Tolerance tolerance )
  159. {
  160. switch (tolerance.Mode)
  161. {
  162. case ToleranceMode.None:
  163. return expected.Equals(actual);
  164. case ToleranceMode.Linear:
  165. decimal decimalTolerance = Convert.ToDecimal(tolerance.Value);
  166. if(decimalTolerance > 0m)
  167. return Math.Abs(expected - actual) <= decimalTolerance;
  168. return expected.Equals( actual );
  169. case ToleranceMode.Percent:
  170. if(expected == 0m)
  171. return expected.Equals(actual);
  172. double relativeError = Math.Abs(
  173. (double)(expected - actual) / (double)expected);
  174. return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
  175. default:
  176. throw new ArgumentException("Unknown tolerance mode specified", "mode");
  177. }
  178. }
  179. private static bool AreEqual( ulong expected, ulong actual, Tolerance tolerance )
  180. {
  181. switch (tolerance.Mode)
  182. {
  183. case ToleranceMode.None:
  184. return expected.Equals(actual);
  185. case ToleranceMode.Linear:
  186. ulong ulongTolerance = Convert.ToUInt64(tolerance.Value);
  187. if(ulongTolerance > 0ul)
  188. {
  189. ulong diff = expected >= actual ? expected - actual : actual - expected;
  190. return diff <= ulongTolerance;
  191. }
  192. return expected.Equals( actual );
  193. case ToleranceMode.Percent:
  194. if (expected == 0ul)
  195. return expected.Equals(actual);
  196. // Can't do a simple Math.Abs() here since it's unsigned
  197. ulong difference = Math.Max(expected, actual) - Math.Min(expected, actual);
  198. double relativeError = Math.Abs( (double)difference / (double)expected );
  199. return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
  200. default:
  201. throw new ArgumentException("Unknown tolerance mode specified", "mode");
  202. }
  203. }
  204. private static bool AreEqual( long expected, long actual, Tolerance tolerance )
  205. {
  206. switch (tolerance.Mode)
  207. {
  208. case ToleranceMode.None:
  209. return expected.Equals(actual);
  210. case ToleranceMode.Linear:
  211. long longTolerance = Convert.ToInt64(tolerance.Value);
  212. if(longTolerance > 0L)
  213. return Math.Abs(expected - actual) <= longTolerance;
  214. return expected.Equals( actual );
  215. case ToleranceMode.Percent:
  216. if(expected == 0L)
  217. return expected.Equals(actual);
  218. double relativeError = Math.Abs(
  219. (double)(expected - actual) / (double)expected);
  220. return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
  221. default:
  222. throw new ArgumentException("Unknown tolerance mode specified", "mode");
  223. }
  224. }
  225. private static bool AreEqual( uint expected, uint actual, Tolerance tolerance )
  226. {
  227. switch (tolerance.Mode)
  228. {
  229. case ToleranceMode.None:
  230. return expected.Equals(actual);
  231. case ToleranceMode.Linear:
  232. uint uintTolerance = Convert.ToUInt32(tolerance.Value);
  233. if(uintTolerance > 0)
  234. {
  235. uint diff = expected >= actual ? expected - actual : actual - expected;
  236. return diff <= uintTolerance;
  237. }
  238. return expected.Equals( actual );
  239. case ToleranceMode.Percent:
  240. if(expected == 0u)
  241. return expected.Equals(actual);
  242. // Can't do a simple Math.Abs() here since it's unsigned
  243. uint difference = Math.Max(expected, actual) - Math.Min(expected, actual);
  244. double relativeError = Math.Abs((double)difference / (double)expected );
  245. return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
  246. default:
  247. throw new ArgumentException("Unknown tolerance mode specified", "mode");
  248. }
  249. }
  250. private static bool AreEqual( int expected, int actual, Tolerance tolerance )
  251. {
  252. switch (tolerance.Mode)
  253. {
  254. case ToleranceMode.None:
  255. return expected.Equals(actual);
  256. case ToleranceMode.Linear:
  257. int intTolerance = Convert.ToInt32(tolerance.Value);
  258. if (intTolerance > 0)
  259. return Math.Abs(expected - actual) <= intTolerance;
  260. return expected.Equals(actual);
  261. case ToleranceMode.Percent:
  262. if (expected == 0)
  263. return expected.Equals(actual);
  264. double relativeError = Math.Abs(
  265. (double)(expected - actual) / (double)expected);
  266. return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0);
  267. default:
  268. throw new ArgumentException("Unknown tolerance mode specified", "mode");
  269. }
  270. }
  271. #endregion
  272. #region Numeric Comparisons
  273. /// <summary>
  274. /// Compare two numeric values, performing the usual numeric conversions.
  275. /// </summary>
  276. /// <param name="expected">The expected value</param>
  277. /// <param name="actual">The actual value</param>
  278. /// <returns>The relationship of the values to each other</returns>
  279. public static int Compare( object expected, object actual )
  280. {
  281. if( !IsNumericType( expected ) || !IsNumericType( actual ) )
  282. throw new ArgumentException( "Both arguments must be numeric");
  283. if ( IsFloatingPointNumeric(expected) || IsFloatingPointNumeric(actual) )
  284. return Convert.ToDouble(expected).CompareTo(Convert.ToDouble(actual));
  285. if ( expected is decimal || actual is decimal )
  286. return Convert.ToDecimal(expected).CompareTo(Convert.ToDecimal(actual));
  287. if ( expected is ulong || actual is ulong )
  288. return Convert.ToUInt64(expected).CompareTo(Convert.ToUInt64(actual));
  289. if ( expected is long || actual is long )
  290. return Convert.ToInt64(expected).CompareTo(Convert.ToInt64(actual));
  291. if ( expected is uint || actual is uint )
  292. return Convert.ToUInt32(expected).CompareTo(Convert.ToUInt32(actual));
  293. return Convert.ToInt32(expected).CompareTo(Convert.ToInt32(actual));
  294. }
  295. #endregion
  296. private Numerics()
  297. {
  298. }
  299. }
  300. }