Esempio n. 1
0
        /// <summary>
        /// Returns the next representable value after x in the direction of y.
        /// </summary>
        /// <param name="x">the value to get the next of</param>
        /// <param name="y">direction</param>
        /// <returns>
        /// The next floating point value in the direction of y
        /// <para>If x == NaN || x == Infinity, returns NaN</para>
        /// <para>If y == NaN, returns NaN</para>
        /// <para>If y &gt; x, returns FloatNext(x)</para>
        /// <para>If y = x, returns x</para>
        /// <para>If y &lt; x, returns FloatPrior(x)</para>
        /// </returns>
        public static double Nextafter(double x, double y)
        {
            if ((double.IsNaN(x) || double.IsInfinity(x)) ||
                (double.IsNaN(y)))
            {
                Policies.ReportDomainError("Nextafter(x: {0}, y: {1}): Requires finite x, y not NaN", x, y);
                return(double.NaN);
            }

            if (y > x)
            {
                return(Math2.FloatNext(x));
            }
            if (y == x)
            {
                return(x);
            }
            return(Math2.FloatPrior(x));
        }
Esempio n. 2
0
        public static double ImpQ(double z, double p, double q)
        {
            Debug.Assert(q <= 0.5);

            if (q == 0)
            {
                return(DoubleLimits.MinNormalValue);
            }

#if EXTRA_DEBUG
            var gs = new Stack <double>();
#endif

            // Set outside limits
            // Since Q(a, z) > 1/2 for a >= z+1, so maxA < z+1
            double minA = DoubleLimits.MinNormalValue;
            double maxA = z + 1;
            if (maxA == z)
            {
                maxA = Math2.FloatNext(z);
            }


            double step   = 0.5;
            double aGuess = 0;

            // We can use the relationship between the incomplete
            // gamma function and the poisson distribution to
            // calculate an approximate inverse.
            // Mostly it is pretty accurate, except when a is small or q is tiny.

            // Case 1: a <= 1 : Inverse Cornish Fisher is unreliable in this area

            var e = Math.Exp(-z);
            if (q <= e)
            {
                if (q == e)
                {
                    return(1);
                }

                maxA = 1;

                // If a <= 1, P(a, x) = 1 - Q(a, x) >= (1-e^-x)^a
                var den   = (z > 0.5) ? Math2.Log1p(-Math.Exp(-z)) : Math.Log(-Math2.Expm1(-z));
                var limit = Math2.Log1p(-q) / den;
                if (limit < 1)
                {
                    minA = Math.Max(minA, limit);
                }

                step   = (1 - minA) * 0.125;
                aGuess = minA * (1 + step);

                if (minA < 0.20)
                {
                    // Use a first order approximation Q(a, z) at a=0
                    // Mathematica: Assuming[ a >= 0 && z > 0, FunctionExpand[Normal[Series[GammaRegularized[a, z], {a, 0, 1}]]]]
                    // q = -a * ExpIntegralEi[-z]
                    // The smaller a is the better the guess
                    // as z->0 term2/term1->1/2*Log[z], which is adequate for double precision

                    aGuess = -q / Math2.Expint(-z);
                    if (aGuess <= DoubleLimits.MachineEpsilon)
                    {
                        ExtraDebugLog(z, p, q, aGuess, aGuess, step, minA, maxA, 0, "");
                        return(aGuess);
                    }
                    step = aGuess * 0.125;
                }
                else if (minA >= 0.40)
                {
                    // Use a first order approximation Q(a, z) at a=1
                    // Mathematica: Assuming[ a >= 0 && z > 0, FunctionExpand[Normal[Series[GammaRegularized[a, z], {a, 1, 1}]]]]

                    var d = Math.Exp(-z) * (Math.Log(z) + Constants.EulerMascheroni) + Math2.Expint(1, z);
                    aGuess = (q - Math.Exp(-z)) / d + 1;
                }

                aGuess = Math.Max(aGuess, minA);
                aGuess = Math.Min(aGuess, maxA);

                var r = SolveA(z, p, q, aGuess, step, minA, maxA);

                ExtraDebugLog(z, p, q, r.Result, aGuess, step, minA, maxA, r.Iterations, "");

                return(r.Result);
            }


            // Case 2: a > 1 : Inverse Poisson Corning Fisher approximation improves as a->Infinity

            minA   = 1;
            aGuess = 0.5 + InversePoissonCornishFisher(z, q, p);
            if (aGuess > 100)
            {
                step = 0.01;
            }
            else if (aGuess > 10)
            {
                step = 0.01;
            }
            else
            {
                // our poisson approximation is weak.
                step = 0.05;
            }


#if EXTRA_DEBUG
            double og = aGuess;
#endif

            aGuess = Math.Max(aGuess, minA);
            aGuess = Math.Min(aGuess, maxA);

#if EXTRA_DEBUG
            if (og != aGuess)
            {
                gs.Push(og);
            }
#endif

            // For small values of q, our Inverse Poisson Cornish Fisher guess can be way off
            // Try to come up with a better guess
            if (q < DoubleLimits.MachineEpsilon)
            {
                step *= 10;
                if (Math.Abs((aGuess - 1) / z) < 1.0 / 8)
                {
#if EXTRA_DEBUG
                    double old = aGuess;
#endif
                    (aGuess, _) = RefineQ_SmallA_LargeZ(z, p, q, aGuess);
#if EXTRA_DEBUG
                    if (old != aGuess)
                    {
                        gs.Push(old);
                    }
#endif
                }
            }

            var rr = SolveA(z, p, q, aGuess, step, minA, maxA);

#if EXTRA_DEBUG
            if (rr.Iterations > 6)
            {
                string guesses = (gs.Count == 0) ? string.Empty : ", og: (" + string.Join(", ", gs) + ")";
                ExtraDebugLog(z, p, q, rr.Result, aGuess, step, minA, maxA, rr.Iterations, guesses);
            }
#endif

            return(rr.Result);
        }