Abs() public static method

Gets the absolute value of a BigInteger object.
public static Abs ( BigInteger b ) : BigInteger
b BigInteger A number.
return BigInteger
コード例 #1
0
        /// <summary>
        /// Modifies the initial estimate until the closest double-precision number to the desired
        /// value is found.
        /// </summary>
        /// <param name="initialEstimate"> The initial estimate.  Assumed to be very close to the
        /// result. </param>
        /// <param name="base10Exponent"> The power-of-ten scale factor. </param>
        /// <param name="desiredValue"> The desired value, already scaled using the power-of-ten
        /// scale factor. </param>
        /// <returns> The closest double-precision number to the desired value.  If there are two
        /// such values, the one with the least significant bit set to zero is returned. </returns>
        private static double RefineEstimate(double initialEstimate, int base10Exponent, BigInteger desiredValue)
        {
            // Numbers with 16 digits or more are tricky because rounding error can cause the
            // result to be out by one or more ULPs (units in the last place).
            // The algorithm is as follows:
            // 1.  Use the initially calculated result as an estimate.
            // 2.  Create a second estimate by modifying the estimate by one ULP.
            // 3.  Calculate the actual value of both estimates to precision X (using arbitrary
            //     precision arithmetic).
            // 4.  If they are both above the desired value then decrease the estimates by 1
            //     ULP and goto step 3.
            // 5.  If they are both below the desired value then increase up the estimates by
            //     1 ULP and goto step 3.
            // 6.  One estimate must now be above the desired value and one below.
            // 7.  If one is estimate is clearly closer to the desired value than the other,
            //     then return that estimate.
            // 8.  Increase the precision by 32 bits.
            // 9.  If the precision is less than or equal to 160 bits goto step 3.
            // 10. Assume that the estimates are equally close to the desired value; return the
            //     value with the least significant bit equal to 0.
            int direction = double.IsPositiveInfinity(initialEstimate) ? -1 : 1;
            int precision = 32;

            // Calculate the candidate value by modifying the last bit.
            double result  = initialEstimate;
            double result2 = AddUlps(result, direction);

            // Figure out our multiplier.  Either base10Exponent is positive, in which case we
            // multiply actual1 and actual2, or it's negative, in which case we multiply
            // desiredValue.
            BigInteger multiplier = BigInteger.One;

            if (base10Exponent < 0)
            {
                multiplier = BigInteger.Pow(10, -base10Exponent);
            }
            else if (base10Exponent > 0)
            {
                desiredValue = BigInteger.Multiply(desiredValue, BigInteger.Pow(10, base10Exponent));
            }

            while (precision <= 160)
            {
                // Scale the candidate values to a big integer.
                var actual1 = ScaleToInteger(result, multiplier, precision);
                var actual2 = ScaleToInteger(result2, multiplier, precision);

                // Calculate the differences between the candidate values and the desired value.
                var baseline = BigInteger.LeftShift(desiredValue, precision);
                var diff1    = BigInteger.Subtract(actual1, baseline);
                var diff2    = BigInteger.Subtract(actual2, baseline);

                if (diff1.Sign == direction && diff2.Sign == direction)
                {
                    // We're going the wrong way!
                    direction = -direction;
                    result2   = AddUlps(result, direction);
                }
                else if (diff1.Sign == -direction && diff2.Sign == -direction)
                {
                    // Going the right way, but need to go further.
                    result  = result2;
                    result2 = AddUlps(result, direction);
                }
                else
                {
                    // Found two values that bracket the actual value.
                    // If one candidate value is closer to the actual value by at least 2 (one
                    // doesn't cut it because of the integer division) then use that value.
                    diff1 = BigInteger.Abs(diff1);
                    diff2 = BigInteger.Abs(diff2);
                    if (BigInteger.Compare(diff1, BigInteger.Subtract(diff2, BigInteger.One)) < 0)
                    {
                        return(result);
                    }
                    if (BigInteger.Compare(diff2, BigInteger.Subtract(diff1, BigInteger.One)) < 0)
                    {
                        return(result2);
                    }

                    // Not enough precision to determine the correct answer, or it's a halfway case.
                    // Increase the precision.
                    precision += 32;
                }

                // If result2 is NaN then we have gone too far.
                if (double.IsNaN(result2) == true)
                {
                    return(result);
                }
            }

            // Even with heaps of precision there is no clear winner.
            // Assume this is a halfway case: choose the floating-point value with its least
            // significant bit equal to 0.
            return((BitConverter.DoubleToInt64Bits(result) & 1) == 0 ? result : result2);
        }