/**
         * @brief Compares two floating point numbers to see if they're equal.
         * @details This method makes use of both an absolute margin of error and an ULP tolerance. If the difference between two
         * values is equal to or smaller than the margin of error, the two values are equal. If they're outside the margin of error,
         * then the difference in ULP between the numbers is calculated. One ULP is the smallest representable change between two
         * floating point numbers, and the actual size of the ULP changes with the scale of the values. This means that ULP can more
         * accurately compare two values for equality than a straight epsilon, or difference comparison. Unfortunately it breaks down
         * near 0 and in several other edge cases, which is why the margin of error check is performed first. For more information on
         * how this method is implemented, see http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/.
         * @param first The first number.
         * @param second The second number.
         * @param marginOfError The acceptable margin of error between the two numbers; any difference between the two numbers smaller
         * than this equates to equal. As a general rule, this should be a very small value.
         * @param ulpTolerance The maximum difference in ULP (unit of least precision) allowed between the two numbers.
         * @returns `true` if considered equal, `false` otherwise.
         * */
        public static bool AreEqual(double first, double second, double marginOfError, int ulpTolerance)
        {
            // When numbers are very close to zero, this initial check of absolute values is needed. Otherwise
            // we can safely use the ULP difference.
            double absoluteDiff = Math.Abs(first - second);

            if (absoluteDiff <= marginOfError)
            {
                return(true);
            }

            DoubleInfo firstInfo  = new DoubleInfo(first);
            DoubleInfo secondInfo = new DoubleInfo(second);

            // Different signs mean the numbers don't match, period.
            if (firstInfo.IsNegative != secondInfo.IsNegative)
            {
                return(false);
            }

            // Find the difference in ULPs (unit of least precision).
            long ulpDiff = Math.Abs(firstInfo.Bits - secondInfo.Bits);

            if (ulpDiff <= ulpTolerance)
            {
                return(true);
            }

            return(false);
        }
        /**
         * @brief Calculates the boundary for the given value.
         * @details A boundary in this context is an interval that all fuzzy numbers with the same parameters are rounded to.
         * This is important for generating a hash for values that use fuzzy comparison.
         * @param value The value to calculate the boundary of.
         * @param marginOfError The margin of error.
         * @param ulpTolerance The allowed tolerance in number of ULP.
         * @param boundaryScale Factor to scale the boundary by; values other than 1 break the transitivity of equality.
         * @returns The boundary interval of the given value.
         * */
        public static long Boundary(double value, double marginOfError, int ulpTolerance, int boundaryScale)
        {
            DoubleInfo valueInfo          = new DoubleInfo(Math.Abs(value));
            DoubleInfo valueWithErrorInfo = new DoubleInfo(Math.Abs(value) + marginOfError);

            long ulpBoundary    = ulpTolerance * boundaryScale;
            long marginBoundary = (valueWithErrorInfo.Bits - valueInfo.Bits) * boundaryScale;

            return(Math.Max(ulpBoundary, marginBoundary));
        }
        /**
         * @brief Calculates the number of ULP between two numbers.
         * @exception System.ArgumentException Arguments have mixed signs (both must be positive or negative).
         * @param first The first number.
         * @param second The second number.
         * @returns The number of ULP between the two numbers.
         * */
        public static long ULPDistance(double first, double second)
        {
            DoubleInfo firstInfo  = new DoubleInfo(first);
            DoubleInfo secondInfo = new DoubleInfo(second);

            if (firstInfo.IsNegative != secondInfo.IsNegative)
            {
                throw new ArgumentException("Numbers have mixed signs; cannot calculate the ULP distance across the 0 boundary.");
            }

            return(Math.Abs(firstInfo.Bits - secondInfo.Bits));
        }
        /**
         * @brief Returns the size of an ULP of the argument.
         * @details Behaves similar to Java's Math.ulp(float value).
         *
         * Special Cases:
         *		- If the argument is NaN, then the result is NaN.
         *		- If the argument is positive or negative infinity, then the result is positive infinity.
         *		- If the argument is positive or negative zero, then the result is Double.Epsilon.
         *		- If the argument is Double.MaxValue or Double.MinValue, then the result is equal to 2^971.
         * @param value The value to get the ULP size of.
         * @returns The ULP size.
         * */
        public static double ULP(double value)
        {
            if (Double.IsNaN(value))
            {
                return(Double.NaN);
            }
            else if (Double.IsPositiveInfinity(value) || Double.IsNegativeInfinity(value))
            {
                return(Double.PositiveInfinity);
            }
            else if (value == 0.0)
            {
                return(Double.Epsilon);
            }
            else if (Math.Abs(value) == Double.MaxValue)
            {
                return(MaxDoubleULP);
            }

            DoubleInfo info = new DoubleInfo(value);

            return(Math.Abs((double)(info.Bits + 1) - value));
        }
 public DoubleInfo(double value)
 {
     _value = value;
     _bits  = DoubleInfo.DoubleToInt64Bits(value);
 }