Beispiel #1
0
        private static void CalculateEOnly(int numBits)
        {
            PrecisionSpec extendedPrecision = new PrecisionSpec(numBits + 1, PrecisionSpec.BaseType.BIN);
            PrecisionSpec normalPrecision = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN);

            int iExponent = (int)(Math.Sqrt(numBits));

            BigFloat factorial = new BigFloat(1, extendedPrecision);
            BigFloat constant = new BigFloat(1, extendedPrecision);
            constant.exponent -= iExponent;
            BigFloat numerator = new BigFloat(constant);
            BigFloat reciprocal;

            //Calculate the 2^iExponent th root of e
            BigFloat e = new BigFloat(1, extendedPrecision);

            int i;
            for (i = 1; i < Int32.MaxValue; i++)
            {
                BigFloat number = new BigFloat(i, extendedPrecision);
                factorial.Mul(number);
                reciprocal = factorial.Reciprocal();
                reciprocal.Mul(numerator);

                if (-reciprocal.exponent > numBits) break;

                e.Add(reciprocal);
                numerator.Mul(constant);
                System.GC.Collect();
            }

            for (i = 0; i < iExponent; i++)
            {
                numerator.Assign(e);
                e.Mul(numerator);
            }

            //Set the cached static values.
            eCache = new BigFloat(e, normalPrecision);
            eRCPCache = new BigFloat(e.Reciprocal(), normalPrecision);
        }
Beispiel #2
0
        /// <summary>
        /// Uses the Gauss-Legendre formula for pi
        /// Taken from http://en.wikipedia.org/wiki/Gauss%E2%80%93Legendre_algorithm
        /// </summary>
        /// <param name="numBits"></param>
        private static void CalculatePi(int numBits)
        {
            int bits = numBits + 32;
            //Precision extend taken out.
            PrecisionSpec normalPres = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN);
            PrecisionSpec extendedPres = new PrecisionSpec(bits, PrecisionSpec.BaseType.BIN);

            if (scratch.Precision.NumBits != bits)
            {
                scratch = new BigInt(extendedPres);
            }

            //a0 = 1
            BigFloat an = new BigFloat(1, extendedPres);

            //b0 = 1/sqrt(2)
            BigFloat bn = new BigFloat(2, extendedPres);
            bn.Sqrt();
            bn.exponent--;

            //to = 1/4
            BigFloat tn = new BigFloat(1, extendedPres);
            tn.exponent -= 2;

            int pn = 0;

            BigFloat anTemp = new BigFloat(extendedPres);

            int iteration = 0;
            int cutoffBits = numBits >> 5;

            for (iteration = 0; ; iteration++)
            {
                //Save a(n)
                anTemp.Assign(an);

                //Calculate new an
                an.Add(bn);
                an.exponent--;

                //Calculate new bn
                bn.Mul(anTemp);
                bn.Sqrt();

                //Calculate new tn
                anTemp.Sub(an);
                anTemp.mantissa.SquareHiFast(scratch);
                anTemp.exponent += anTemp.exponent + pn + 1 - anTemp.mantissa.Normalise();
                tn.Sub(anTemp);

                anTemp.Assign(an);
                anTemp.Sub(bn);

                if (anTemp.exponent < -(bits - cutoffBits)) break;

                //New pn
                pn++;
            }

            an.Add(bn);
            an.mantissa.SquareHiFast(scratch);
            an.exponent += an.exponent + 1 - an.mantissa.Normalise();
            tn.exponent += 2;
            an.Div(tn);

            pi = new BigFloat(an, normalPres);
            piBy2 = new BigFloat(pi);
            piBy2.exponent--;
            twoPi = new BigFloat(pi, normalPres);
            twoPi.exponent++;
            piRecip = new BigFloat(an.Reciprocal(), normalPres);
            twoPiRecip = new BigFloat(piRecip);
            twoPiRecip.exponent--;
            //1/3 is going to be useful for sin.
            threeRecip = new BigFloat((new BigFloat(3, extendedPres)).Reciprocal(), normalPres);
        }
Beispiel #3
0
        private static BigFloat TenPow(int power, PrecisionSpec precision)
        {
            BigFloat acc = new BigFloat(1, precision);
            BigFloat temp = new BigFloat(1, precision);

            int powerTemp = power;

            BigFloat multiplierToUse = new BigFloat(10, precision);

            if (power < 0)
            {
                multiplierToUse = multiplierToUse.Reciprocal();
                powerTemp = -power;
            }

            //Fast power function
            while (powerTemp != 0)
            {
                temp.Mul(multiplierToUse);
                multiplierToUse.Assign(temp);

                if ((powerTemp & 1) != 0)
                {
                    acc.Mul(temp);
                }

                powerTemp >>= 1;
            }

            return acc;
        }
Beispiel #4
0
        private static BigFloat R(BigFloat a0, BigFloat b0)
        {
            //Precision extend taken out.
            int bits = a0.mantissa.Precision.NumBits;
            PrecisionSpec extendedPres = new PrecisionSpec(bits, PrecisionSpec.BaseType.BIN);
            BigFloat an = new BigFloat(a0, extendedPres);
            BigFloat bn = new BigFloat(b0, extendedPres);
            BigFloat sum = new BigFloat(extendedPres);
            BigFloat term = new BigFloat(extendedPres);
            BigFloat temp1 = new BigFloat(extendedPres);
            BigFloat one = new BigFloat(1, extendedPres);

            int iteration = 0;

            for (iteration = 0; ; iteration++)
            {
                //Get the sum term for this iteration.
                term.Assign(an);
                term.Mul(an);
                temp1.Assign(bn);
                temp1.Mul(bn);
                //term = an^2 - bn^2
                term.Sub(temp1);
                //term = 2^(n-1) * (an^2 - bn^2)
                term.exponent += iteration - 1;
                sum.Add(term);

                if (term.exponent < -(bits - 8)) break;

                //Calculate the new AGM estimates.
                temp1.Assign(an);
                an.Add(bn);
                //a(n+1) = (an + bn) / 2
                an.MulPow2(-1);

                //b(n+1) = sqrt(an*bn)
                bn.Mul(temp1);
                bn.Sqrt();
            }

            one.Sub(sum);
            one = one.Reciprocal();
            return new BigFloat(one, a0.mantissa.Precision);
        }
Beispiel #5
0
        private void Exp(int numBits)
        {
            if (IsSpecialValue)
            {
                if (SpecialValue == SpecialValueType.ZERO)
                {
                    //e^0 = 1
                    exponent = 0;
                    mantissa.SetHighDigit(0x80000000);
                }
                else if (SpecialValue == SpecialValueType.INF_MINUS)
                {
                    //e^-inf = 0
                    SetZero();
                }

                return;
            }

            PrecisionSpec prec = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN);
            numBits = prec.NumBits;

            if (scratch.Precision.NumBits != prec.NumBits)
            {
                scratch = new BigInt(prec);
            }

            if (inverseFactorialCache == null || invFactorialCutoff < numBits)
            {
                CalculateFactorials(numBits);
            }

            //let x = 1 * 'this'.mantissa (i.e. 1 <= x < 2)
            //exp(2^n * x) = e^(2^n * x) = (e^x)^2n = exp(x)^2n

            int oldExponent = 0;

            if (exponent > -4)
            {
                oldExponent = exponent + 4;
                exponent = -4;
            }

            BigFloat thisSave = new BigFloat(this, prec);
            BigFloat temp = new BigFloat(1, prec);
            BigFloat temp2 = new BigFloat(this, prec);
            BigFloat res = new BigFloat(1, prec);
            int length = inverseFactorialCache.Length;

            int iterations;
            for (int i = 1; i < length; i++)
            {
                //temp = x^i
                temp.Mul(thisSave);
                temp2.Assign(inverseFactorialCache[i]);
                temp2.Mul(temp);

                if (temp2.exponent < -(numBits + 4)) { iterations = i; break; }

                res.Add(temp2);
            }

            //res = exp(x)
            //Now... x^(2^n) = (x^2)^(2^(n - 1))
            for (int i = 0; i < oldExponent; i++)
            {
                res.mantissa.SquareHiFast(scratch);
                int shift = res.mantissa.Normalise();
                res.exponent = res.exponent << 1;
                res.exponent += 1 - shift;
            }

            //Deal with +/- inf
            if (res.exponent == Int32.MaxValue)
            {
                res.mantissa.Zero();
            }

            Assign(res);
        }
Beispiel #6
0
        /// <summary>
        /// Calculates ln(2) and returns -10^(n/2 + a bit) for reuse, using the AGM method as described in
        /// http://lacim.uqam.ca/~plouffe/articles/log2.pdf
        /// </summary>
        /// <param name="numBits"></param>
        /// <returns></returns>
        private static void CalculateLog2(int numBits)
        {
            //Use the AGM method formula to get log2 to N digits.
            //R(a0, b0) = 1 / (1 - Sum(2^-n*(an^2 - bn^2)))
            //log(1/2) = R(1, 10^-n) - R(1, 10^-n/2)
            PrecisionSpec normalPres = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN);
            PrecisionSpec extendedPres = new PrecisionSpec(numBits + 1, PrecisionSpec.BaseType.BIN);
            BigFloat a0 = new BigFloat(1, extendedPres);
            BigFloat b0 = TenPow(-(int)((double)((numBits >> 1) + 2) * 0.302), extendedPres);
            BigFloat pow10saved = new BigFloat(b0);
            BigFloat firstAGMcacheSaved = new BigFloat(extendedPres);

            //save power of 10 (in normal precision)
            pow10cache = new BigFloat(b0, normalPres);

            ln2cache = R(a0, b0);

            //save the first half of the log calculation
            firstAGMcache = new BigFloat(ln2cache, normalPres);
            firstAGMcacheSaved.Assign(ln2cache);

            b0.MulPow2(-1);
            ln2cache.Sub(R(a0, b0));

            //Convert to log(2)
            ln2cache.mantissa.Sign = false;

            //Save magic constant for newton log
            //First guess in range 1 <= x < 2 is x0 = ln2 * (x - 1) + C
            logNewtonConstant = new BigFloat(ln2cache);
            logNewtonConstant.Mul(new BigFloat(3, extendedPres));
            logNewtonConstant.exponent--;
            logNewtonConstant.Sub(new BigFloat(1, extendedPres));
            logNewtonConstant = new BigFloat(logNewtonConstant, normalPres);

            //Save the inverse.
            log2ecache = new BigFloat(ln2cache);
            log2ecache = new BigFloat(log2ecache.Reciprocal(), normalPres);

            //Now cache log10
            //Because the log functions call this function to the precision to which they
            //are called, we cannot call them without causing an infinite loop, so we need
            //to inline the code.
            log10recip = new BigFloat(10, extendedPres);

            {
                int power2 = log10recip.exponent + 1;
                log10recip.exponent = -1;

                //BigFloat res = new BigFloat(firstAGMcache);
                BigFloat ax = new BigFloat(1, extendedPres);
                BigFloat bx = new BigFloat(pow10saved);
                bx.Mul(log10recip);

                BigFloat r = R(ax, bx);

                log10recip.Assign(firstAGMcacheSaved);
                log10recip.Sub(r);

                ax.Assign(ln2cache);
                ax.Mul(new BigFloat(power2, log10recip.mantissa.Precision));
                log10recip.Add(ax);
            }

            log10recip = log10recip.Reciprocal();
            log10recip = new BigFloat(log10recip, normalPres);


            //Trim to n bits
            ln2cache = new BigFloat(ln2cache, normalPres);
        }
Beispiel #7
0
        /// <summary>
        /// Tried the newton method for logs, but the exponential function is too slow to do it.
        /// </summary>
        private void LogNewton()
        {
            if (mantissa.IsZero() || mantissa.Sign)
            {
                return;
            }

            //Compute ln2.
            if (ln2cache == null || mantissa.Precision.NumBits > ln2cache.mantissa.Precision.NumBits)
            {
                CalculateLog2(mantissa.Precision.NumBits);
            }

            int numBits = mantissa.Precision.NumBits;

            //Use inverse exp function with Newton's method.
            BigFloat xn = new BigFloat(this);
            BigFloat oldExponent = new BigFloat(xn.exponent, mantissa.Precision);
            xn.exponent = 0;
            this.exponent = 0;
            //Hack to subtract 1
            xn.mantissa.ClearBit(numBits - 1);
            //x0 = (x - 1) * log2 - this is a straight line fit between log(1) = 0 and log(2) = ln2
            xn.Mul(ln2cache);
            //x0 = (x - 1) * log2 + C - this corrects for minimum error over the range.
            xn.Add(logNewtonConstant);
            BigFloat term = new BigFloat(mantissa.Precision);
            BigFloat one = new BigFloat(1, mantissa.Precision);

            int precision = 32;
            int normalPrecision = mantissa.Precision.NumBits;

            int iterations = 0;

            while (true)
            {
                term.Assign(xn);
                term.mantissa.Sign = true;
                term.Exp(precision);
                term.Mul(this);
                term.Sub(one);

                iterations++;
                if (term.exponent < -((precision >> 1) - 4))
                {
                    if (precision == normalPrecision)
                    {
                        if (term.exponent < -(precision - 4)) break;
                    }
                    else
                    {
                        precision = precision << 1;
                        if (precision > normalPrecision) precision = normalPrecision;
                    }
                }

                xn.Add(term);
            }

            //log(2^n*s) = log(2^n) + log(s) = nlog(2) + log(s)
            term.Assign(ln2cache);
            term.Mul(oldExponent);

            this.Assign(xn);
            this.Add(term);
        }
Beispiel #8
0
        /// <summary>
        /// arctan(): the inverse function of sin(), range of (-pi/2..pi/2)
        /// </summary>
        public void Arctan()
        {
            //With 2 argument reductions, we increase precision by a minimum of 4 bits per term.
            int numBits = mantissa.Precision.NumBits;
            int maxTerms = numBits >> 2;

            if (pi == null || pi.mantissa.Precision.NumBits != numBits)
            {
                CalculatePi(mantissa.Precision.NumBits);
            }

            //Make domain positive
            bool sign = mantissa.Sign;
            mantissa.Sign = false;

            if (IsSpecialValue)
            {
                if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.INF_PLUS)
                {
                    Assign(piBy2);
                    mantissa.Sign = sign;
                    return;
                }

                return;
            }

            if (reciprocals == null || reciprocals[0].mantissa.Precision.NumBits != numBits || reciprocals.Length < maxTerms)
            {
                CalculateReciprocals(numBits, maxTerms);
            }

            bool invert = false;
            BigFloat one = new BigFloat(1, mantissa.Precision);

            //Invert if outside of convergence
            if (GreaterThan(one))
            {
                invert = true;
                Assign(Reciprocal());
            }

            //Reduce using half-angle formula:
            //arctan(2x) = 2 arctan (x / (1 + sqrt(1 + x)))

            //First reduction (guarantees 2 bits per iteration)
            BigFloat temp = new BigFloat(this);
            temp.Mul(this);
            temp.Add(one);
            temp.Sqrt();
            temp.Add(one);
            this.Div(temp);

            //Second reduction (guarantees 4 bits per iteration)
            temp.Assign(this);
            temp.Mul(this);
            temp.Add(one);
            temp.Sqrt();
            temp.Add(one);
            this.Div(temp);

            //Actual series calculation
            int length = reciprocals.Length;
            BigFloat term = new BigFloat(this);

            //pow = x^2
            BigFloat pow = new BigFloat(this);
            pow.Mul(this);

            BigFloat sum = new BigFloat(this);

            for (int i = 1; i < length; i++)
            {
                //u(n) = u(n-1) * x^2
                //t(n) = u(n) / (2n+1)
                term.Mul(pow);
                term.Sign = !term.Sign;
                temp.Assign(term);
                temp.Mul(reciprocals[i]);

                if (temp.exponent < -numBits) break;

                sum.Add(temp);
            }

            //Undo the reductions.
            Assign(sum);
            exponent += 2;

            if (invert)
            {
                //Assign(Reciprocal());
                mantissa.Sign = true;
                Add(piBy2);
            }

            if (sign)
            {
                mantissa.Sign = sign;
            }
        }
Beispiel #9
0
 /// <summary>
 /// Multiplies two numbers and assigns the result to res.
 /// </summary>
 /// <param name="res">a pre-existing BigFloat to take the result</param>
 /// <param name="n1">the first number</param>
 /// <param name="n2">the second number</param>
 /// <returns>a handle to res</returns>
 public static BigFloat Mul(BigFloat res, BigFloat n1, BigFloat n2)
 {
     res.Assign(n1);
     res.Mul(n2);
     return res;
 }
Beispiel #10
0
 /// <summary>
 /// Divides two numbers and assigns the result to res.
 /// </summary>
 /// <param name="res">a pre-existing BigFloat to take the result</param>
 /// <param name="n1">the first number</param>
 /// <param name="n2">the second number</param>
 /// <returns>a handle to res</returns>
 public static BigFloat Div(BigFloat res, BigFloat n1, BigFloat n2)
 {
     res.Assign(n1);
     res.Div(n2);
     return res;
 }
Beispiel #11
0
 /// <summary>
 /// Subtracts two numbers and assigns the result to res.
 /// </summary>
 /// <param name="res">a pre-existing BigFloat to take the result</param>
 /// <param name="n1">the first number</param>
 /// <param name="n2">the second number</param>
 /// <returns>a handle to res</returns>
 public static BigFloat Sub(BigFloat res, BigFloat n1, BigFloat n2)
 {
     res.Assign(n1);
     res.Sub(n2);
     return res;
 }
Beispiel #12
0
        //******************* Fast static functions ********************

        /// <summary>
        /// Adds two numbers and assigns the result to res.
        /// </summary>
        /// <param name="res">a pre-existing BigFloat to take the result</param>
        /// <param name="n1">the first number</param>
        /// <param name="n2">the second number</param>
        /// <returns>a handle to res</returns>
        public static BigFloat Add(BigFloat res, BigFloat n1, BigFloat n2)
        {
            res.Assign(n1);
            res.Add(n2);
            return res;
        }
Beispiel #13
0
        /// <summary>
        /// Two-variable iterative square root, taken from
        /// http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#A_two-variable_iterative_method
        /// </summary>
        public void Sqrt()
        {
            if (mantissa.Sign || IsSpecialValue)
            {
                if (SpecialValue == SpecialValueType.ZERO)
                {
                    return;
                }

                if (SpecialValue == SpecialValueType.INF_MINUS || mantissa.Sign)
                {
                    SetNaN();
                }

                return;
            }

            BigFloat temp2;
            BigFloat temp3 = new BigFloat(mantissa.Precision);
            BigFloat three = new BigFloat(3, mantissa.Precision);

            int exponentScale = 0;

            //Rescale to 0.5 <= x < 2
            if (exponent < -1)
            {
                int diff = -exponent;
                if ((diff & 1) != 0)
                {
                    diff--;
                }

                exponentScale = -diff;
                exponent += diff;
            }
            else if (exponent > 0)
            {
                if ((exponent & 1) != 0)
                {
                    exponentScale = exponent + 1;
                    exponent = -1;
                }
                else
                {
                    exponentScale = exponent;
                    exponent = 0;
                }
            }

            temp2 = new BigFloat(this);
            temp2.Sub(new BigFloat(1, mantissa.Precision));

            //if (temp2.mantissa.IsZero())
            //{
            //    exponent += exponentScale;
            //    return;
            //}

            int numBits = mantissa.Precision.NumBits;

            while ((exponent - temp2.exponent) < numBits && temp2.SpecialValue != SpecialValueType.ZERO)
            {
                //a(n+1) = an - an*cn / 2
                temp3.Assign(this);
                temp3.Mul(temp2);
                temp3.MulPow2(-1);
                this.Sub(temp3);

                //c(n+1) = cn^2 * (cn - 3) / 4
                temp3.Assign(temp2);
                temp2.Sub(three);
                temp2.Mul(temp3);
                temp2.Mul(temp3);
                temp2.MulPow2(-2);
            }

            exponent += (exponentScale >> 1);
        }
Beispiel #14
0
        //******************** Mathematical Constants *******************

        /// <summary>
        /// Gets pi to the indicated precision
        /// </summary>
        /// <param name="precision">The precision to perform the calculation to</param>
        /// <returns>pi (the ratio of the area of a circle to its diameter)</returns>
        public static BigFloat GetPi(PrecisionSpec precision)
        {
            if (pi == null || precision.NumBits <= pi.mantissa.Precision.NumBits)
            {
                CalculatePi(precision.NumBits);
            }

            BigFloat ret = new BigFloat (precision);
            ret.Assign(pi);

            return ret;
        }
Beispiel #15
0
        /// <summary>
        /// Log(x) implemented as an Arithmetic-Geometric Mean. Fast for high precisions.
        /// </summary>
        private void LogAGM1()
        {
            if (mantissa.IsZero() || mantissa.Sign)
            {
                return;
            }

            //Compute ln2.
            if (ln2cache == null || mantissa.Precision.NumBits > ln2cache.mantissa.Precision.NumBits)
            {
                CalculateLog2(mantissa.Precision.NumBits);
            }

            //Compute ln(x) using AGM formula

            //1. Re-write the input as 2^n * (0.5 <= x < 1)
            int power2 = exponent + 1;
            exponent = -1;

            //BigFloat res = new BigFloat(firstAGMcache);
            BigFloat a0 = new BigFloat(1, mantissa.Precision);
            BigFloat b0 = new BigFloat(pow10cache);
            b0.Mul(this);

            BigFloat r = R(a0, b0);

            this.Assign(firstAGMcache);
            this.Sub(r);

            a0.Assign(ln2cache);
            a0.Mul(new BigFloat(power2, mantissa.Precision));
            this.Add(a0);
        }
Beispiel #16
0
        /// <summary>
        /// Get e to the indicated precision
        /// </summary>
        /// <param name="precision">The preicision to perform the calculation to</param>
        /// <returns>e (the number for which the d/dx(e^x) = e^x)</returns>
        public static BigFloat GetE(PrecisionSpec precision)
        {
            if (eCache == null || eCache.mantissa.Precision.NumBits < precision.NumBits)
            {
                CalculateEOnly(precision.NumBits);
                //CalculateFactorials(precision.NumBits);
            }

            BigFloat ret = new BigFloat(precision);
            ret.Assign(eCache);

            return ret;
        }
Beispiel #17
0
        /// <summary>
        /// Calculates Sin(x):
        /// This takes a little longer and is less accurate if the input is out of the range (-pi, pi].
        /// </summary>
        public void Sin()
        {
            if (IsSpecialValue)
            {
                //Sin(x) has no limit as x->inf
                if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.INF_PLUS)
                {
                    SetNaN();
                }

                return;
            }

            //Convert to positive range (0 <= x < inf)
            bool sign = mantissa.Sign;
            mantissa.Sign = false;

            if (pi == null || pi.mantissa.Precision.NumBits != mantissa.Precision.NumBits)
            {
                CalculatePi(mantissa.Precision.NumBits);
            }

            if (inverseFactorialCache == null || invFactorialCutoff != mantissa.Precision.NumBits)
            {
                CalculateFactorials(mantissa.Precision.NumBits);
            }

            //Rescale into 0 <= x < 2*pi
            if (GreaterThan(twoPi))
            {
                //There will be an inherent loss of precision doing this.
                BigFloat newAngle = new BigFloat(this);
                newAngle.Mul(twoPiRecip);
                newAngle.FPart();
                newAngle.Mul(twoPi);
                Assign(newAngle);
            }

            //Rescale into range 0 <= x < pi
            if (GreaterThan(pi))
            {
                //sin(pi + a) = sin(pi)cos(a) + sin(a)cos(pi) = 0 - sin(a) = -sin(a)
                Sub(pi);
                sign = !sign;
            }

            BigFloat temp = new BigFloat(mantissa.Precision);

            //Rescale into range 0 <= x < pi/2
            if (GreaterThan(piBy2))
            {
                temp.Assign(this);
                Assign(pi);
                Sub(temp);
            }

            //Rescale into range 0 <= x < pi/6 to accelerate convergence.
            //This is done using sin(3x) = 3sin(x) - 4sin^3(x)
            Mul(threeRecip);

            if (mantissa.IsZero())
            {
                exponent = 0;
                return;
            }

            BigFloat term = new BigFloat(this);

            BigFloat square = new BigFloat(this);
            square.Mul(term);

            BigFloat sum = new BigFloat(this);

            bool termSign = true;
            int length = inverseFactorialCache.Length;
            int numBits = mantissa.Precision.NumBits;

            for (int i = 3; i < length; i += 2)
            {
                term.Mul(square);
                temp.Assign(inverseFactorialCache[i]);
                temp.Mul(term);
                temp.mantissa.Sign = termSign;
                termSign = !termSign;

                if (temp.exponent < -numBits) break;

                sum.Add(temp);
            }

            //Restore the triple-angle: sin(3x) = 3sin(x) - 4sin^3(x)
            Assign(sum);
            sum.Mul(this);
            sum.Mul(this);
            Mul(new BigFloat(3, mantissa.Precision));
            sum.exponent += 2;
            Sub(sum);

            //Restore the sign
            mantissa.Sign = sign;
        }