/// <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); }
/// <summary> /// Two-variable iterative square root, taken from /// http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#A_two-variable_iterative_method /// </summary> /// <remarks>This is quite a fast function, as elementary functions go. You can expect it to take /// about twice as long as a floating-point division. /// </remarks> public static BigFloat Sqrt(BigFloat n1) { BigFloat res = new BigFloat(n1); n1.Sqrt(); return n1; }
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); }
/// <summary> /// Arccosh(): the inverse cosh() function /// </summary> public void Arccosh() { //acosh isn't defined for x < 1 if (IsSpecialValue) { if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.ZERO) { SetNaN(); return; } return; } BigFloat one = new BigFloat(1, mantissa.Precision); if (LessThan(one)) { SetNaN(); return; } BigFloat temp = new BigFloat(this); temp.Mul(this); temp.Sub(one); temp.Sqrt(); Add(temp); Log(); }
/// <summary> /// Arcsinh(): the inverse sinh function /// </summary> public void Arcsinh() { //Just let all special values fall through if (IsSpecialValue) { return; } BigFloat temp = new BigFloat(this); temp.Mul(this); temp.Add(new BigFloat(1, mantissa.Precision)); temp.Sqrt(); Add(temp); Log(); }
/// <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; } }
/// <summary> /// arcsin(): the inverse function of sin(), range of (-pi/2..pi/2) /// </summary> public void Arcsin() { if (IsSpecialValue) { if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.INF_PLUS || SpecialValue == SpecialValueType.NAN) { SetNaN(); return; } return; } BigFloat one = new BigFloat(1, mantissa.Precision); BigFloat plusABit = new BigFloat(1, mantissa.Precision); plusABit.exponent -= (mantissa.Precision.NumBits - (mantissa.Precision.NumBits >> 6)); BigFloat onePlusABit = new BigFloat(1, mantissa.Precision); onePlusABit.Add(plusABit); bool sign = mantissa.Sign; mantissa.Sign = false; if (GreaterThan(onePlusABit)) { SetNaN(); } else if (LessThan(one)) { BigFloat temp = new BigFloat(this); temp.Mul(this); temp.Sub(one); temp.mantissa.Sign = !temp.mantissa.Sign; temp.Sqrt(); temp.Add(one); Div(temp); Arctan(); exponent++; mantissa.Sign = sign; } else { if (pi == null || pi.mantissa.Precision.NumBits != mantissa.Precision.NumBits) { CalculatePi(mantissa.Precision.NumBits); } Assign(piBy2); if (sign) mantissa.Sign = true; } }
/// <summary> /// Calculates tan(x) /// </summary> public void Tan() { if (IsSpecialValue) { //Tan(x) has no limit as x->inf if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.INF_PLUS) { SetNaN(); } else if (SpecialValue == SpecialValueType.ZERO) { SetZero(); } return; } if (pi == null || pi.mantissa.Precision.NumBits != mantissa.Precision.NumBits) { CalculatePi(mantissa.Precision.NumBits); } //Work out the sign change (involves replicating some rescaling). bool sign = mantissa.Sign; mantissa.Sign = false; if (mantissa.IsZero()) { return; } //Rescale into 0 <= x < pi if (GreaterThan(pi)) { //There will be an inherent loss of precision doing this. BigFloat newAngle = new BigFloat(this); newAngle.Mul(piRecip); newAngle.FPart(); newAngle.Mul(pi); Assign(newAngle); } //Rescale to -pi/2 <= x < pi/2 if (!LessThan(piBy2)) { Sub(pi); } //Now the sign of the sin determines the sign of the tan. //tan(x) = sin(x) / sqrt(1 - sin^2(x)) Sin(); BigFloat denom = new BigFloat(this); denom.Mul(this); denom.Sub(new BigFloat(1, mantissa.Precision)); denom.mantissa.Sign = !denom.mantissa.Sign; if (denom.mantissa.Sign) { denom.SetZero(); } denom.Sqrt(); Div(denom); if (sign) mantissa.Sign = !mantissa.Sign; }